pencil2001

python-examples

主要章节和小节重新按照如下逻辑划分:

  • 一、Python基础

    • 1 数字
    • 2 字符串
    • 3 列表
    • 4 流程控制
    • 5 编程风格
    • 6 函数
    • 7 输入和输出
    • 8 数据结构
    • 9 模块
    • 10 错误和异常
    • 11 类和对象

  • 二、Python模块

    • 1 时间模块
    • 2 文件操作
    • 3 常见迭代器
    • 4 yield 用法
    • 5 装饰器用法
    • 6 枚举 用法
    • 7 列表生成式
    • 8 字典生成式
    • 9 lambda 表达式
    • 10 正则 常用

  • 三、Python绘图

    • matplotlib 常用
    • seaborn 常用
    • plotly 常用
    • pyecharts 常用
    • turtle 常用
    • 三维绘图

  • 四、Python 数据分析

  • 五、Python 基础算法

    • 数组
    • 链表
    • 递归
    • 排序和搜索
    • 动态规划
    • 字符串
    • 其他

  • 六、机器学习

    • 机器学习本质
    • boosting 系列
    • 神经网络系列
    • 强化学习系列
    • 推荐系统
    • CV 路线
    • NLP 系列

一、Python基础

Python基础主要总结Python常用内置函数;Python独有的语法特性、关键词nonlocalglobal等;内置数据结构包括:列表(list), 字典(dict), 集合(set), 元组(tuple) 以及相关的高级模块collections中的Counternamedtupledefaultdictheapq模块。目前共有90个小例子。

1 求绝对值

绝对值或复数的模

>>> abs(-6)
6

2 元素都为真

接受一个可迭代对象,如果可迭代对象的所有元素都为真,那么返回 True,否则返回False

有0,所以不是所有元素都为真

>>> all([1,0,3,6])
False

所有元素都为真

>>> all([1,2,3])
True

3 元素至少一个为真 

接受一个可迭代对象,如果可迭代对象里至少有一个元素为真,那么返回True,否则返回False

没有一个元素为真

>>> any([0,0,0,[]])
False

至少一个元素为真:

>>> any([0,0,1])
True

4 ascii展示对象  

调用对象的 __repr__ 方法,获得该方法的返回值,如下例子返回值为字符串

>>> class Student():
    def __init__(self,id,name):
        self.id = id
        self.name = name
    def __repr__(self):
        return \'id = \'+self.id +\', name = \'+self.name

调用:

>>> xiaoming = Student(id=\'1\',name=\'xiaoming\')
>>> xiaoming
id = 1, name = xiaoming
>>> ascii(xiaoming)
\'id = 1, name = xiaoming\'

5 十转二

将十进制转换为二进制:

>>> bin(10)
\'0b1010\'

6 十转八

十进制转换为八进制:

>>> oct(9)
\'0o11\'

7 十转十六

十进制转换为十六进制:

>>> hex(15)
\'0xf\'

8 判断是真是假  

测试一个对象是True, 还是False.

>>> bool([0,0,0])
True
>>> bool([])
False
>>> bool([1,0,1])
True

9 字符串转字节  

字符串转换为字节类型

>>> s = "apple"
>>> bytes(s,encoding=\'utf-8\')
b\'apple\'

10 转为字符串  

数值型等转换为字符串类型

>>> i = 100
>>> str(i)
\'100\'

11 是否可调用  

判断对象是否可被调用

In [1]: callable(str)
Out[1]: True

In [2]: callable(int)
Out[2]: True

In [3]: xiaoming
Out[3]: id = 001, name = xiaoming

In [4]: callable(xiaoming)
Out[4]: False

如果想让xiaoming能被调用 xiaoming(), 需要重写Student类的__call__方法:

In [1]: class Student():
    ...:     def __init__(self,id,name):
    ...:         self.id = id
    ...:         self.name = name
    ...:     def __repr__(self):
    ...:         return \'id = \'+self.id +\', name = \'+self.name
    ...:     def __call__(self):
    ...:         print(\'I can be called\')
    ...:         print(f\'my name is {self.name}\')
    ...: 
    ...: 

In [2]: t = Student(\'001\',\'xiaoming\')

In [3]: t()
I can be called
my name is xiaoming

12 十转ASCII

查看十进制整数对应的ASCII字符

In [1]: chr(65)
Out[1]: \'A\'

13 ASCII转十

查看某个ASCII字符对应的十进制数

In [1]: ord(\'A\')
Out[1]: 65

14 类方法 

classmethod 装饰器对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

In [1]: class Student():
    ...:     def __init__(self,id,name):
    ...:         self.id = id
    ...:         self.name = name
    ...:     def __repr__(self):
    ...:         return \'id = \'+self.id +\', name = \'+self.name
    ...:     @classmethod
    ...:     def f(cls):
    ...:         print(cls)

15 执行字符串表示的代码

将字符串编译成python能识别或可执行的代码,也可以将文字读成字符串再编译。

In [1]: s  = "print(\'helloworld\')"
    
In [2]: r = compile(s,"<string>", "exec")
    
In [3]: r
Out[3]: <code object <module> at 0x0000000005DE75D0, file "<string>", line 1>
    
In [4]: exec(r)
helloworld

16 创建复数

创建一个复数

In [1]: complex(1,2)
Out[1]: (1+2j)

17 动态删除属性  

删除对象的属性

In [1]: delattr(xiaoming,\'id\')

In [2]: hasattr(xiaoming,\'id\')
Out[2]: False

18 转为字典  

创建数据字典

In [1]: dict()
Out[1]: {}

In [2]: dict(a=\'a\',b=\'b\')
Out[2]: {\'a\': \'a\', \'b\': \'b\'}

In [3]: dict(zip([\'a\',\'b\'],[1,2]))
Out[3]: {\'a\': 1, \'b\': 2}

In [4]: dict([(\'a\',1),(\'b\',2)])
Out[4]: {\'a\': 1, \'b\': 2}

19 一键查看对象所有方法 

不带参数时返回当前范围内的变量、方法和定义的类型列表;带参数时返回参数的属性,方法列表。

In [96]: dir(xiaoming)
Out[96]:
[\'__class__\',
 \'__delattr__\',
 \'__dict__\',
 \'__dir__\',
 \'__doc__\',
 \'__eq__\',
 \'__format__\',
 \'__ge__\',
 \'__getattribute__\',
 \'__gt__\',
 \'__hash__\',
 \'__init__\',
 \'__init_subclass__\',
 \'__le__\',
 \'__lt__\',
 \'__module__\',
 \'__ne__\',
 \'__new__\',
 \'__reduce__\',
 \'__reduce_ex__\',
 \'__repr__\',
 \'__setattr__\',
 \'__sizeof__\',
 \'__str__\',
 \'__subclasshook__\',
 \'__weakref__\',
 
 \'name\']

20 取商和余数  

分别取商和余数

In [1]: divmod(10,3)
Out[1]: (3, 1)

21 枚举对象  

返回一个可以枚举的对象,该对象的next()方法将返回一个元组。

In [1]: s = ["a","b","c"]
    ...: for i ,v in enumerate(s,1):
    ...:     print(i,v)
    ...:
1 a
2 b
3 c

22 计算表达式

将字符串str 当成有效的表达式来求值并返回计算结果取出字符串中内容

In [1]: s = "1 + 3 +5"
    ...: eval(s)
    ...:
Out[1]: 9

23 查看变量所占字节数

In [1]: import sys

In [2]: a = {\'a\':1,\'b\':2.0}

In [3]: sys.getsizeof(a) # 占用240个字节
Out[3]: 240

24 过滤器  

在函数中设定过滤条件,迭代元素,保留返回值为True的元素:

In [1]: fil = filter(lambda x: x>10,[1,11,2,45,7,6,13])

In [2]: list(fil)
Out[2]: [11, 45, 13]

25 转为浮点类型 

将一个整数或数值型字符串转换为浮点数

In [1]: float(3)
Out[1]: 3.0

如果不能转化为浮点数,则会报ValueError:

In [2]: float(\'a\')
# ValueError: could not convert string to float: \'a\'

26 字符串格式化 

格式化输出字符串,format(value, format_spec)实质上是调用了value的__format__(format_spec)方法。

In [104]: print("i am {0},age{1}".format("tom",18))
i am tom,age18
3.1415926 {:.2f} 3.14 保留小数点后两位
3.1415926 {:+.2f} +3.14 带符号保留小数点后两位
-1 {:+.2f} -1.00 带符号保留小数点后两位
2.71828 {:.0f} 3 不带小数
5 {:0>2d} 05 数字补零 (填充左边, 宽度为2)
5 {:x<4d} 5xxx 数字补x (填充右边, 宽度为4)
10 {:x<4d} 10xx 数字补x (填充右边, 宽度为4)
1000000 {:,} 1,000,000 以逗号分隔的数字格式
0.25 {:.2%} 25.00% 百分比格式
1000000000 {:.2e} 1.00e+09 指数记法
18 {:>10d} \' 18\' 右对齐 (默认, 宽度为10)
18 {:<10d} \'18 \' 左对齐 (宽度为10)
18 {:^10d} \' 18 \' 中间对齐 (宽度为10)

27 冻结集合

创建一个不可修改的集合。

In [1]: frozenset([1,1,3,2,3])
Out[1]: frozenset({1, 2, 3})

因为不可修改,所以没有像set那样的addpop方法

28 动态获取对象属性 

获取对象的属性

In [1]: class Student():
   ...:     def __init__(self,id,name):
   ...:         self.id = id
   ...:         self.name = name
   ...:     def __repr__(self):
   ...:         return \'id = \'+self.id +\', name = \'+self.name

In [2]: xiaoming = Student(id=\'001\',name=\'xiaoming\')
In [3]: getattr(xiaoming,\'name\') # 获取xiaoming这个实例的name属性值
Out[3]: \'xiaoming\'

29 对象是否有这个属性

In [1]: class Student():
   ...:     def __init__(self,id,name):
   ...:         self.id = id
   ...:         self.name = name
   ...:     def __repr__(self):
   ...:         return \'id = \'+self.id +\', name = \'+self.name

In [2]: xiaoming = Student(id=\'001\',name=\'xiaoming\')
In [3]: hasattr(xiaoming,\'name\')
Out[3]: True

In [4]: hasattr(xiaoming,\'address\')
Out[4]: False

30 返回对象的哈希值  

返回对象的哈希值,值得注意的是自定义的实例都是可哈希的,listdictset等可变对象都是不可哈希的(unhashable)

In [1]: hash(xiaoming)
Out[1]: 6139638

In [2]: hash([1,2,3])
# TypeError: unhashable type: \'list\'

31 一键帮助 

返回对象的帮助文档

In [1]: help(xiaoming)
Help on Student in module __main__ object:

class Student(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self, id, name)
 |
 |  __repr__(self)
 |
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

32 对象门牌号 

返回对象的内存地址

In [1]: id(xiaoming)
Out[1]: 98234208

33 获取用户输入 

获取用户输入内容

In [1]: input()
aa
Out[1]: \'aa\'

34 转为整型  

int(x, base =10) , x可能为字符串或数值,将x 转换为一个普通整数。如果参数是字符串,那么它可能包含符号和小数点。如果超出了普通整数的表示范围,一个长整数被返回。

In [1]: int(\'12\',16)
Out[1]: 18

35 isinstance

判断object是否为类classinfo的实例,是返回true

In [1]: class Student():
   ...:     def __init__(self,id,name):
   ...:         self.id = id
   ...:         self.name = name
   ...:     def __repr__(self):
   ...:         return \'id = \'+self.id +\', name = \'+self.name

In [2]: xiaoming = Student(id=\'001\',name=\'xiaoming\')

In [3]: isinstance(xiaoming,Student)
Out[3]: True

36 父子关系鉴定

In [1]: class undergraduate(Student):
    ...:     def studyClass(self):
    ...:         pass
    ...:     def attendActivity(self):
    ...:         pass

In [2]: issubclass(undergraduate,Student)
Out[2]: True

In [3]: issubclass(object,Student)
Out[3]: False

In [4]: issubclass(Student,object)
Out[4]: True

如果class是classinfo元组中某个元素的子类,也会返回True

In [1]: issubclass(int,(int,float))
Out[1]: True

37 创建迭代器类型

使用iter(obj, sentinel), 返回一个可迭代对象, sentinel可省略(一旦迭代到此元素,立即终止)

In [1]: lst = [1,3,5]

In [2]: for i in iter(lst):
    ...:     print(i)
    ...:
1
3
5
In [1]: class TestIter(object):
    ...:     def __init__(self):
    ...:         self.l=[1,3,2,3,4,5]
    ...:         self.i=iter(self.l)
    ...:     def __call__(self):  #定义了__call__方法的类的实例是可调用的
    ...:         item = next(self.i)
    ...:         print ("__call__ is called,fowhich would return",item)
    ...:         return item
    ...:     def __iter__(self): #支持迭代协议(即定义有__iter__()函数)
    ...:         print ("__iter__ is called!!")
    ...:         return iter(self.l)
In [2]: t = TestIter()
In [3]: t() # 因为实现了__call__,所以t实例能被调用
__call__ is called,which would return 1
Out[3]: 1

In [4]: for e in TestIter(): # 因为实现了__iter__方法,所以t能被迭代
    ...:     print(e)
    ...: 
__iter__ is called!!
1
3
2
3
4
5

38 所有对象之根

object 是所有类的基类

In [1]: o = object()

In [2]: type(o)
Out[2]: object

39 打开文件

返回文件对象

In [1]: fo = open(\'D:/a.txt\',mode=\'r\', encoding=\'utf-8\')

In [2]: fo.read()
Out[2]: \'\ufefflife is not so long,\nI use Python to play.\'

mode取值表:

字符 意义
\'r\' 读取(默认)
\'w\' 写入,并先截断文件
\'x\' 排它性创建,如果文件已存在则失败
\'a\' 写入,如果文件存在则在末尾追加
\'b\' 二进制模式
\'t\' 文本模式(默认)
\'+\' 打开用于更新(读取与写入)

40 次幂

base为底的exp次幂,如果mod给出,取余

In [1]: pow(3, 2, 4)
Out[1]: 1

41 打印

In [5]: lst = [1,3,5]

In [6]: print(lst)
[1, 3, 5]

In [7]: print(f\'lst: {lst}\')
lst: [1, 3, 5]

In [8]: print(\'lst:{}\'.format(lst))
lst:[1, 3, 5]

In [9]: print(\'lst:\',lst)
lst: [1, 3, 5]

42 创建属性的两种方式

返回 property 属性,典型的用法:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x
    # 使用property类创建 property 属性
    x = property(getx, setx, delx, "I\'m the \'x\' property.")

使用python装饰器,实现与上完全一样的效果代码:

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

43 创建range序列

  1. range(stop)
  2. range(start, stop[,step])

生成一个不可变序列:

In [1]: range(11)
Out[1]: range(0, 11)

In [2]: range(0,11,1)
Out[2]: range(0, 11)

44 反向迭代器

In [1]: rev = reversed([1,4,2,3,1])

In [2]: for i in rev:
     ...:     print(i)
     ...:
1
3
2
4
1

45 四舍五入

四舍五入,ndigits代表小数点后保留几位:

In [11]: round(10.0222222, 3)
Out[11]: 10.022

In [12]: round(10.05,1)
Out[12]: 10.1

46 转为集合类型

返回一个set对象,集合内不允许有重复元素:

In [159]: a = [1,4,2,3,1]

In [160]: set(a)
Out[160]: {1, 2, 3, 4}

47 转为切片对象

class slice(startstop[, step])

返回一个表示由 range(start, stop, step) 所指定索引集的 slice对象,它让代码可读性、可维护性变好。

In [1]: a = [1,4,2,3,1]

In [2]: my_slice_meaning = slice(0,5,2)

In [3]: a[my_slice_meaning]
Out[3]: [1, 2, 1]

48 拿来就用的排序函数

排序:

In [1]: a = [1,4,2,3,1]

In [2]: sorted(a,reverse=True)
Out[2]: [4, 3, 2, 1, 1]

In [3]: a = [{\'name\':\'xiaoming\',\'age\':18,\'gender\':\'male\'},{\'name\':\'
     ...: xiaohong\',\'age\':20,\'gender\':\'female\'}]
In [4]: sorted(a,key=lambda x: x[\'age\'],reverse=False)
Out[4]:
[{\'name\': \'xiaoming\', \'age\': 18, \'gender\': \'male\'},
 {\'name\': \'xiaohong\', \'age\': 20, \'gender\': \'female\'}]

####49 求和函数

求和:

In [181]: a = [1,4,2,3,1]

In [182]: sum(a)
Out[182]: 11

In [185]: sum(a,10) #求和的初始值为10
Out[185]: 21

50 转元组

tuple() 将对象转为一个不可变的序列类型

In [16]: i_am_list = [1,3,5]
In [17]: i_am_tuple = tuple(i_am_list)
In [18]: i_am_tuple
Out[18]: (1, 3, 5)

51 查看对象类型

class type(namebasesdict)

传入一个参数时,返回 object 的类型:

In [1]: class Student():
   ...:     def __init__(self,id,name):
   ...:         self.id = id
   ...:         self.name = name
   ...:     def __repr__(self):
   ...:         return \'id = \'+self.id +\', name = \'+self.name
   ...: 
   ...: 

In [2]: xiaoming = Student(id=\'001\',name=\'xiaoming\')
In [3]: type(xiaoming)
Out[3]: __main__.Student

In [4]: type(tuple())
Out[4]: tuple

52 聚合迭代器

创建一个聚合了来自每个可迭代对象中的元素的迭代器:

In [1]: x = [3,2,1]
In [2]: y = [4,5,6]
In [3]: list(zip(y,x))
Out[3]: [(4, 3), (5, 2), (6, 1)]

In [4]: a = range(5)
In [5]: b = list(\'abcde\')
In [6]: b
Out[6]: [\'a\', \'b\', \'c\', \'d\', \'e\']
In [7]: [str(y) + str(x) for x,y in zip(a,b)]
Out[7]: [\'a0\', \'b1\', \'c2\', \'d3\', \'e4\']

53 nonlocal用于内嵌函数中

关键词nonlocal常用于函数嵌套中,声明变量i为非局部变量; 如果不声明,i+=1表明i为函数wrapper内的局部变量,因为在i+=1引用(reference)时,i未被声明,所以会报unreferenced variable的错误。

def excepter(f):
    i = 0
    t1 = time.time()
    def wrapper(): 
        try:
            f()
        except Exception as e:
            nonlocal i
            i += 1
            print(f\'{e.args[0]}: {i}\')
            t2 = time.time()
            if i == n:
                print(f\'spending time:{round(t2-t1,2)}\')
    return wrapper

54 global 声明全局变量

先回答为什么要有global,一个变量被多个函数引用,想让全局变量被所有函数共享。有的伙伴可能会想这还不简单,这样写:

i = 5
def f():
    print(i)

def g():
    print(i)
    pass

f()
g()

f和g两个函数都能共享变量i,程序没有报错,所以他们依然不明白为什么要用global.

但是,如果我想要有个函数对i递增,这样:

def h():
    i += 1

h()

此时执行程序,bang, 出错了! 抛出异常:UnboundLocalError,原来编译器在解释i+=1时会把i解析为函数h()内的局部变量,很显然在此函数内,编译器找不到对变量i的定义,所以会报错。

global就是为解决此问题而被提出,在函数h内,显示地告诉编译器i为全局变量,然后编译器会在函数外面寻找i的定义,执行完i+=1后,i还为全局变量,值加1:

i = 0
def h():
    global i
    i += 1

h()
print(i)

55 链式比较

i = 3
print(1 < i < 3)  # False
print(1 < i <= 3)  # True

56 不用else和if实现计算器

from operator import *


def calculator(a, b, k):
    return {
        \'+\': add,
        \'-\': sub,
        \'*\': mul,
        \'/\': truediv,
        \'**\': pow
    }[k](a, b)


calculator(1, 2, \'+\')  # 3
calculator(3, 4, \'**\')  # 81

57 链式操作

from operator import (add, sub)


def add_or_sub(a, b, oper):
    return (add if oper == \'+\' else sub)(a, b)


add_or_sub(1, 2, \'-\')  # -1

58 交换两元素

def swap(a, b):
    return b, a


print(swap(1, 0))  # (0,1)

59 去最求平均

def score_mean(lst):
    lst.sort()
    lst2=lst[1:(len(lst)-1)]
    return round((sum(lst2)/len(lst2)),1)

lst=[9.1, 9.0,8.1, 9.7, 19,8.2, 8.6,9.8]
score_mean(lst) # 9.1

60 打印99乘法表

打印出如下格式的乘法表

1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
1*4=4   2*4=8   3*4=12  4*4=16
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81

一共有10 行,第i行的第j列等于:j*i

其中,

i取值范围:1<=i<=9

j取值范围:1<=j<=i

根据例子分析的语言描述,转化为如下代码:

for i in range(1,10):
    ...:     for j in range(1,i+1):
    ...:         print(\'%d*%d=%d\'%(j,i,j*i),end="\t")
    ...:     print()

61 全展开

对于如下数组:

[[[1,2,3],[4,5]]]

如何完全展开成一维的。这个小例子实现的flatten是递归版,两个参数分别表示带展开的数组,输出数组。

from collections.abc import *

def flatten(lst, out_lst=None):
    if out_lst is None:
        out_lst = []
    for i in lst:
        if isinstance(i, Iterable): # 判断i是否可迭代
            flatten(i, out_lst)  # 尾数递归
        else:
            out_lst.append(i)    # 产生结果
    return out_lst

调用flatten:

print(flatten([[1,2,3],[4,5]]))
print(flatten([[1,2,3],[4,5]], [6,7]))
print(flatten([[[1,2,3],[4,5,6]]]))
# 结果:
[1, 2, 3, 4, 5]
[6, 7, 1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]

numpy里的flatten与上面的函数实现有些微妙的不同:

import numpy
b = numpy.array([[1,2,3],[4,5]])
b.flatten()
array([list([1, 2, 3]), list([4, 5])], dtype=object)

62 列表等分

from math import ceil

def divide(lst, size):
    if size <= 0:
        return [lst]
    return [lst[i * size:(i+1)*size] for i in range(0, ceil(len(lst) / size))]


r = divide([1, 3, 5, 7, 9], 2)
print(r)  # [[1, 3], [5, 7], [9]]

r = divide([1, 3, 5, 7, 9], 0)
print(r)  # [[1, 3, 5, 7, 9]]

r = divide([1, 3, 5, 7, 9], -3)
print(r)  # [[1, 3, 5, 7, 9]]

63 列表压缩

def filter_false(lst):
    return list(filter(bool, lst))


r = filter_false([None, 0, False, \'\', [], \'ok\', [1, 2]])
print(r)  # [\'ok\', [1, 2]]

64 更长列表

def max_length(*lst):
    return max(*lst, key=lambda v: len(v))


r = max_length([1, 2, 3], [4, 5, 6, 7], [8])
print(f\'更长的列表是{r}\')  # [4, 5, 6, 7]

r = max_length([1, 2, 3], [4, 5, 6, 7], [8, 9])
print(f\'更长的列表是{r}\')  # [4, 5, 6, 7]

65 求众数

def top1(lst):
    return max(lst, default=\'列表为空\', key=lambda v: lst.count(v))

lst = [1, 3, 3, 2, 1, 1, 2]
r = top1(lst)
print(f\'{lst}中出现次数最多的元素为:{r}\')  # [1, 3, 3, 2, 1, 1, 2]中出现次数最多的元素为:1

66 多表之最

def max_lists(*lst):
    return max(max(*lst, key=lambda v: max(v)))


r = max_lists([1, 2, 3], [6, 7, 8], [4, 5])
print(r)  # 8

67 列表查重

def has_duplicates(lst):
    return len(lst) == len(set(lst))


x = [1, 1, 2, 2, 3, 2, 3, 4, 5, 6]
y = [1, 2, 3, 4, 5]
has_duplicates(x)  # False
has_duplicates(y)  # True

68 列表反转

def reverse(lst):
    return lst[::-1]


r = reverse([1, -2, 3, 4, 1, 2])
print(r)  # [2, 1, 4, 3, -2, 1]

69 浮点数等差数列

def rang(start, stop, n):
    start,stop,n = float(\'%.2f\' % start), float(\'%.2f\' % stop),int(\'%.d\' % n)
    step = (stop-start)/n
    lst = [start]
    while n > 0:
        start,n = start+step,n-1
        lst.append(round((start), 2))
    return lst

rang(1, 8, 10) # [1.0, 1.7, 2.4, 3.1, 3.8, 4.5, 5.2, 5.9, 6.6, 7.3, 8.0]

70 按条件分组

def bif_by(lst, f):
    return [ [x for x in lst if f(x)],[x for x in lst if not f(x)]]

records = [25,89,31,34] 
bif_by(records, lambda x: x<80) # [[25, 31, 34], [89]]

71 map实现向量运算

#多序列运算函数—map(function,iterabel,iterable2)
lst1=[1,2,3,4,5,6]
lst2=[3,4,5,6,3,2]
list(map(lambda x,y:x*y+1,lst1,lst2))
### [4, 9, 16, 25, 16, 13]

72 值最大的字典

def max_pairs(dic):
    if len(dic) == 0:
        return dic
    max_val = max(map(lambda v: v[1], dic.items()))
    return [item for item in dic.items() if item[1] == max_val]


r = max_pairs({\'a\': -10, \'b\': 5, \'c\': 3, \'d\': 5})
print(r)  # [(\'b\', 5), (\'d\', 5)]

73 合并两个字典

def merge_dict(dic1, dic2):
    return {**dic1, **dic2}  # python3.5后支持的一行代码实现合并字典

merge_dict({\'a\': 1, \'b\': 2}, {\'c\': 3})  # {\'a\': 1, \'b\': 2, \'c\': 3}

74 topn字典

from heapq import nlargest

# 返回字典d前n个最大值对应的键

def topn_dict(d, n):
    return nlargest(n, d, key=lambda k: d[k])

topn_dict({\'a\': 10, \'b\': 8, \'c\': 9, \'d\': 10}, 3)  # [\'a\', \'d\', \'c\']

75 异位词

from collections import Counter

# 检查两个字符串是否 相同字母异序词,简称:互为变位词

def anagram(str1, str2):
    return Counter(str1) == Counter(str2)

anagram(\'eleven+two\', \'twelve+one\')  # True 这是一对神器的变位词
anagram(\'eleven\', \'twelve\')  # False

76 逻辑上合并字典

(1) 两种合并字典方法 这是一般的字典合并写法

dic1 = {\'x\': 1, \'y\': 2 }
dic2 = {\'y\': 3, \'z\': 4 }
merged1 = {**dic1, **dic2} # {\'x\': 1, \'y\': 3, \'z\': 4}

修改merged[\'x\']=10,dic1中的x值不变merged是重新生成的一个新字典

但是,ChainMap却不同,它在内部创建了一个容纳这些字典的列表。因此使用ChainMap合并字典,修改merged[\'x\']=10后,dic1中的x值改变,如下所示:

from collections import ChainMap
merged2 = ChainMap(dic1,dic2)
print(merged2) # ChainMap({\'x\': 1, \'y\': 2}, {\'y\': 3, \'z\': 4})

77 命名元组提高可读性

from collections import namedtuple
Point = namedtuple(\'Point\', [\'x\', \'y\', \'z\'])  # 定义名字为Point的元祖,字段属性有x,y,z
lst = [Point(1.5, 2, 3.0), Point(-0.3, -1.0, 2.1), Point(1.3, 2.8, -2.5)]
print(lst[0].y - lst[1].y)

使用命名元组写出来的代码可读性更好,尤其处理上百上千个属性时作用更加凸显。

78 样本抽样

使用sample抽样,如下例子从100个样本中随机抽样10个。

from random import randint,sample
lst = [randint(0,50) for _ in range(100)]
print(lst[:5])# [38, 19, 11, 3, 6]
lst_sample = sample(lst,10)
print(lst_sample) # [33, 40, 35, 49, 24, 15, 48, 29, 37, 24]

79 重洗数据集

使用shuffle用来重洗数据集,值得注意shuffle是对lst就地(in place)洗牌,节省存储空间

from random import shuffle
lst = [randint(0,50) for _ in range(100)]
shuffle(lst)
print(lst[:5]) # [50, 3, 48, 1, 26]

80 10个均匀分布的坐标点

random模块中的uniform(a,b)生成[a,b)内的一个随机数,如下生成10个均匀分布的二维坐标点

from random import uniform
In [1]: [(uniform(0,10),uniform(0,10)) for _ in range(10)]
Out[1]: 
[(9.244361194237328, 7.684326645514235),
 (8.129267671737324, 9.988395854203773),
 (9.505278771040661, 2.8650440524834107),
 (3.84320100484284, 1.7687190176304601),
 (6.095385729409376, 2.377133802224657),
 (8.522913365698605, 3.2395995841267844),
 (8.827829601859406, 3.9298809217233766),
 (1.4749644859469302, 8.038753079253127),
 (9.005430657826324, 7.58011186920019),
 (8.700789540392917, 1.2217577293254112)]

81 10个高斯分布的坐标点

random模块中的gauss(u,sigma)生成均值为u, 标准差为sigma的满足高斯分布的值,如下生成10个二维坐标点,样本误差(y-2*x-1)满足均值为0,标准差为1的高斯分布:

from random import gauss
x = range(10)
y = [2*xi+1+gauss(0,1) for xi in x]
points = list(zip(x,y))
### 10个二维点:
[(0, -0.86789025305992),
 (1, 4.738439437453464),
 (2, 5.190278040856102),
 (3, 8.05270893133576),
 (4, 9.979481700775292),
 (5, 11.960781766216384),
 (6, 13.025427054303737),
 (7, 14.02384035204836),
 (8, 15.33755823101161),
 (9, 17.565074449028497)]

82 chain高效串联多个容器对象

chain函数串联a和b,兼顾内存效率同时写法更加优雅。

from itertools import chain
a = [1,3,5,0]
b = (2,4,6)

for i in chain(a,b):
  print(i)
### 结果
1
3
5
0
2
4
6

83 操作函数对象

In [31]: def f():
    ...:     print(\'i\\'m f\')
    ...:

In [32]: def g():
    ...:     print(\'i\\'m g\')
    ...:

In [33]: [f,g][1]()
i\'m g

创建函数对象的list,根据想要调用的index,方便统一调用。

84 生成逆序序列

list(range(10,-1,-1)) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

第三个参数为负时,表示从第一个参数开始递减,终止到第二个参数(不包括此边界)

85 函数的五类参数使用例子

python五类参数:位置参数,关键字参数,默认参数,可变位置或关键字参数的使用。

def f(a,*b,c=10,**d):
  print(f\'a:{a},b:{b},c:{c},d:{d}\')

默认参数c不能位于可变关键字参数d后.

调用f:

In [10]: f(1,2,5,width=10,height=20)
a:1,b:(2, 5),c:10,d:{\'width\': 10, \'height\': 20}

可变位置参数b实参后被解析为元组(2,5);而c取得默认值10; d被解析为字典.

再次调用f:

In [11]: f(a=1,c=12)
a:1,b:(),c:12,d:{}

a=1传入时a就是关键字参数,b,d都未传值,c被传入12,而非默认值。

注意观察参数a, 既可以f(1),也可以f(a=1) 其可读性比第一种更好,建议使用f(a=1)。如果要强制使用f(a=1),需要在前面添加一个星号:

def f(*,a,**b):
  print(f\'a:{a},b:{b}\')

此时f(1)调用,将会报错:TypeError: f() takes 0 positional arguments but 1 was given

只能f(a=1)才能OK.

说明前面的*发挥作用,它变为只能传入关键字参数,那么如何查看这个参数的类型呢?借助python的inspect模块:

In [22]: for name,val in signature(f).parameters.items():
    ...:     print(name,val.kind)
    ...:
a KEYWORD_ONLY
b VAR_KEYWORD

可看到参数a的类型为KEYWORD_ONLY,也就是仅仅为关键字参数。

但是,如果f定义为:

def f(a,*b):
  print(f\'a:{a},b:{b}\')

查看参数类型:

In [24]: for name,val in signature(f).parameters.items():
    ...:     print(name,val.kind)
    ...:
a POSITIONAL_OR_KEYWORD
b VAR_POSITIONAL

可以看到参数a既可以是位置参数也可是关键字参数。

86 使用slice对象

生成关于蛋糕的序列cake1:

In [1]: cake1 = list(range(5,0,-1))

In [2]: b = cake1[1:10:2]

In [3]: b
Out[3]: [4, 2]

In [4]: cake1
Out[4]: [5, 4, 3, 2, 1]

再生成一个序列:

In [5]: from random import randint
   ...: cake2 = [randint(1,100) for _ in range(100)]
   ...: # 同样以间隔为2切前10个元素,得到切片d
   ...: d = cake2[1:10:2]
In [6]: d
Out[6]: [75, 33, 63, 93, 15]

你看,我们使用同一种切法,分别切开两个蛋糕cake1,cake2. 后来发现这种切法极为经典,又拿它去切更多的容器对象。

那么,为什么不把这种切法封装为一个对象呢?于是就有了slice对象。

定义slice对象极为简单,如把上面的切法定义成slice对象:

perfect_cake_slice_way = slice(1,10,2)
#去切cake1
cake1_slice = cake1[perfect_cake_slice_way] 
cake2_slice = cake2[perfect_cake_slice_way]

In [11]: cake1_slice
Out[11]: [4, 2]

In [12]: cake2_slice
Out[12]: [75, 33, 63, 93, 15]

与上面的结果一致。

对于逆向序列切片,slice对象一样可行:

a = [1,3,5,7,9,0,3,5,7]
a_ = a[5:1:-1]

named_slice = slice(5,1,-1)
a_slice = a[named_slice] 

In [14]: a_
Out[14]: [0, 9, 7, 5]

In [15]: a_slice
Out[15]: [0, 9, 7, 5]

频繁使用同一切片的操作可使用slice对象抽出来,复用的同时还能提高代码可读性。

87 lambda 函数的动画演示

有些读者反映,lambda函数不太会用,问我能不能解释一下。

比如,下面求这个 lambda函数:

def max_len(*lists):
    return max(*lists, key=lambda v: len(v))

有两点疑惑:

  • 参数v的取值?
  • lambda函数有返回值吗?如果有,返回值是多少?

调用上面函数,求出以下三个最长的列表:

r = max_len([1, 2, 3], [4, 5, 6, 7], [8])
print(f\'更长的列表是{r}\')

程序完整运行过程,动画演示如下:

结论:

  • 参数v的可能取值为*lists,也就是 tuple 的一个元素。

  • lambda函数返回值,等于lambda v冒号后表达式的返回值。

88 粘性之禅

7 行代码够烧脑,不信试试~~

def product(*args, repeat=1):
    pools = [tuple(pool) for pool in args] * repeat
    result = [[]]
    for pool in pools:
        result = [x+[y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)

调用函数:

rtn = product(\'xyz\', \'12\', repeat=3)
print(list(rtn))

快去手动敲敲,看看输出啥吧~~

89 元类

xiaomingxiaohongxiaozhang 都是学生,这类群体叫做 Student.

Python 定义类的常见方法,使用关键字 class

In [36]: class Student(object):
    ...:     pass

xiaomingxiaohongxiaozhang 是类的实例,则:

xiaoming = Student()
xiaohong = Student()
xiaozhang = Student()

创建后,xiaoming 的 __class__ 属性,返回的便是 Student

In [38]: xiaoming.__class__
Out[38]: __main__.Student

问题在于,Student 类有 __class__属性,如果有,返回的又是什么?

In [39]: xiaoming.__class__.__class__
Out[39]: type

哇,程序没报错,返回 type

那么,我们不妨猜测:Student 类,类型就是 type

换句话说,Student类就是一个对象,它的类型就是 type

所以,Python 中一切皆对象,类也是对象

Python 中,将描述 Student 类的类被称为:元类。

按照此逻辑延伸,描述元类的类被称为:元元类,开玩笑了~ 描述元类的类也被称为元类。

聪明的朋友会问了,既然 Student 类可创建实例,那么 type 类可创建实例吗? 如果能,它创建的实例就叫:类 了。 你们真聪明!

说对了,type 类一定能创建实例,比如 Student 类了。

In [40]: Student = type(\'Student\',(),{})

In [41]: Student
Out[41]: __main__.Student

它与使用 class 关键字创建的 Student 类一模一样。

Python 的类,因为又是对象,所以和 xiaomingxiaohong 对象操作相似。支持:

  • 赋值
  • 拷贝
  • 添加属性
  • 作为函数参数
In [43]: StudentMirror = Student # 类直接赋值 # 类直接赋值
In [44]: Student.class_property = \'class_property\' # 添加类属性
In [46]: hasattr(Student, \'class_property\')
Out[46]: True

元类,确实使用不是那么多,也许先了解这些,就能应付一些场合。就连 Python 界的领袖 Tim Peters 都说:

“元类就是深度的魔法,99%的用户应该根本不必为此操心。”

90 对象序列化

对象序列化,是指将内存中的对象转化为可存储或传输的过程。很多场景,直接一个类对象,传输不方便。

但是,当对象序列化后,就会更加方便,因为约定俗成的,接口间的调用或者发起的 web 请求,一般使用 json 串传输。

实际使用中,一般对类对象序列化。先创建一个 Student 类型,并创建两个实例。

    class Student():
        def __init__(self,**args):
            self.ids = args[\'ids\']
            self.name = args[\'name\']
            self.address = args[\'address\']
    xiaoming = Student(ids = 1,name = \'xiaoming\',address = \'北京\')
    xiaohong = Student(ids = 2,name = \'xiaohong\',address = \'南京\')

导入 json 模块,调用 dump 方法,就会将列表对象 [xiaoming,xiaohong],序列化到文件 json.txt 中。

    import json
    
    with open(\'json.txt\', \'w\') as f:
        json.dump([xiaoming,xiaohong], f, default=lambda obj: obj.__dict__,\ 
ensure_ascii=False, indent=2, sort_keys=True)

生成的文件内容,如下:

    [
      {
        "address": "北京",
        "ids": 1,
        "name": "xiaoming"
      },
      {
        "address": "南京",
        "ids": 2,
        "name": "xiaohong"
      }
    ]

二、Python字符串和正则

字符串无所不在,字符串的处理也是最常见的操作。本章节将总结和字符串处理相关的一切操作。主要包括基本的字符串操作;高级字符串操作之正则。目前共有25个小例子

91 反转字符串

st="python"
#方法1
\'\'.join(reversed(st))
#方法2
st[::-1]

92 字符串切片操作

字符串切片操作——查找替换3或5的倍数
In [1]:[str("java"[i%3*4:]+"python"[i%5*6:] or i) for i in range(1,15)]
OUT[1]:[\'1\',
 \'2\',
 \'java\',
 \'4\',
 \'python\',
 \'java\',
 \'7\',
 \'8\',
 \'java\',
 \'python\',
 \'11\',
 \'java\',
 \'13\',
 \'14\']

93 join串联字符串

In [4]: mystr = [\'1\',
   ...:  \'2\',
   ...:  \'java\',
   ...:  \'4\',
   ...:  \'python\',
   ...:  \'java\',
   ...:  \'7\',
   ...:  \'8\',
   ...:  \'java\',
   ...:  \'python\',
   ...:  \'11\',
   ...:  \'java\',
   ...:  \'13\',
   ...:  \'14\']

In [5]: \',\'.join(mystr) #用逗号连接字符串
Out[5]: \'1,2,java,4,python,java,7,8,java,python,11,java,13,14\'

94 字符串的字节长度

def str_byte_len(mystr):
    return (len(mystr.encode(\'utf-8\')))


str_byte_len(\'i love python\')  # 13(个字节)
str_byte_len(\'字符\')  # 6(个字节)

以下是正则部分

import re

95 查找第一个匹配串

s = \'i love python very much\'
pat = \'python\' 
r = re.search(pat,s)
print(r.span()) #(7,13)

96 查找所有1的索引

s = \'山东省潍坊市青州第1中学高三1班\'
pat = \'1\'
r = re.finditer(pat,s)
for i in r:
    print(i)

# <re.Match object; span=(9, 10), match=\'1\'>
# <re.Match object; span=(14, 15), match=\'1\'>

97 \d 匹配数字[0-9]

findall找出全部位置的所有匹配

s = \'一共20行代码运行时间13.59s\'
pat = r\'\d+\' # +表示匹配数字(\d表示数字的通用字符)1次或多次
r = re.findall(pat,s)
print(r)
# [\'20\', \'13\', \'59\']

98 匹配浮点数和整数

?表示前一个字符匹配0或1次

s = \'一共20行代码运行时间13.59s\'
pat = r\'\d+\.?\d+\' # ?表示匹配小数点(\.)0次或1次,这种写法有个小bug,不能匹配到个位数的整数
r = re.findall(pat,s)
print(r)
# [\'20\', \'13.59\']

# 更好的写法:
pat = r\'\d+\.\d+|\d+\' # A|B,匹配A失败才匹配B

99 ^匹配字符串的开头

s = \'This module provides regular expression matching operations similar to those found in Perl\'
pat = r\'^[emrt]\' # 查找以字符e,m,r或t开始的字符串
r = re.findall(pat,s)
print(r)
# [],因为字符串的开头是字符`T`,不在emrt匹配范围内,所以返回为空
IN [11]: s2 = \'email for me is guozhennianhua@163.com\'
re.findall(\'^[emrt].*\',s2)# 匹配以e,m,r,t开始的字符串,后面是多个任意字符
Out[11]: [\'email for me is guozhennianhua@163.com\']

100 re.I 忽略大小写

s = \'That\'
pat = r\'t\' 
r = re.findall(pat,s,re.I)
In [22]: r
Out[22]: [\'T\', \'t\']

101 理解compile的作用

如果要做很多次匹配,可以先编译匹配串:

import re
pat = re.compile(\'\W+\') # \W 匹配不是数字和字母的字符
has_special_chars = pat.search(\'ed#2@edc\') 
if has_special_chars:
    print(f\'str contains special characters:{has_special_chars.group(0)}\')

###输出结果: 
 # str contains special characters:#   

### 再次使用pat正则编译对象 做匹配
again_pattern = pat.findall(\'guozhennianhua@163.com\')
if \'@\' in again_pattern:
    print(\'possibly it is an email\')

102 使用()捕获单词,不想带空格

使用()捕获

s = \'This module provides regular expression matching operations similar to those found in Perl\'
pat = r\'\s([a-zA-Z]+)\'  
r = re.findall(pat,s)
print(r) #[\'module\', \'provides\', \'regular\', \'expression\', \'matching\', \'operations\',\
\'similar\', \'to\', \'those\', \'found\', \'in\', \'Perl\']

看到提取单词中未包括第一个单词,使用?表示前面字符出现0次或1次,但是此字符还有表示贪心或非贪心匹配含义,使用时要谨慎。

s = \'This module provides regular expression matching operations similar to those found in Perl\'
pat = r\'\s?([a-zA-Z]+)\'  
r = re.findall(pat,s)
print(r) #[\'This\', \'module\', \'provides\', \'regular\', \'expression\', \'matching\', \'operations\',\
\'similar\', \'to\', \'those\', \'found\', \'in\', \'Perl\']

103 split分割单词

使用以上方法分割单词不是简洁的,仅仅是为了演示。分割单词最简单还是使用split函数。

s = \'This module provides regular expression matching operations similar to those found in Perl\'
pat = r\'\s+\'  
r = re.split(pat,s)
print(r) # [\'This\', \'module\', \'provides\', \'regular\', \'expression\', \'matching\', \'operations\',\
\'similar\', \'to\', \'those\', \'found\', \'in\', \'Perl\'] ### 上面这句话也可直接使用str自带的split函数: s.split(\' \') #使用空格分隔 ### 但是,对于风格符更加复杂的情况,split无能为力,只能使用正则 s = \'This,,, module ; \t provides|| regular ; \' words = re.split(\'[,\s;|]+\',s) #这样分隔出来,最后会有一个空字符串 words = [i for i in words if len(i)>0]

104 match从字符串开始位置匹配

注意match,search等的不同:

  1. match函数
import re
### match
mystr = \'This\'
pat = re.compile(\'hi\')
pat.match(mystr) # None
pat.match(mystr,1) # 从位置1处开始匹配
Out[90]: <re.Match object; span=(1, 3), match=\'hi\'>
  1. search函数 search是从字符串的任意位置开始匹配
In [91]: mystr = \'This\'
    ...: pat = re.compile(\'hi\')
    ...: pat.search(mystr)
Out[91]: <re.Match object; span=(1, 3), match=\'hi\'>

105 替换匹配的子串

sub函数实现对匹配子串的替换

content="hello 12345, hello 456321"    
pat=re.compile(r\'\d+\') #要替换的部分
m=pat.sub("666",content)
print(m) # hello 666, hello 666

106 贪心捕获

(.*)表示捕获任意多个字符,尽可能多的匹配字符

content=\'<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc\'
pat=re.compile(r"<div>(.*)</div>")  #贪婪模式
m=pat.findall(content)
print(m) #匹配结果为: [\'graph</div>bb<div>math\']

107 非贪心捕获

仅添加一个问号(?),得到结果完全不同,这是非贪心匹配,通过这个例子体会贪心和非贪心的匹配的不同。

content=\'<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc\'
pat=re.compile(r"<div>(.*?)</div>")
m=pat.findall(content)
print(m) # [\'graph\', \'math\']

非贪心捕获,见好就收。

108 常用元字符总结

. 匹配任意字符  
^ 匹配字符串开始位置 
$ 匹配字符串中结束的位置 
* 前面的原子重复0次、1次、多次 
? 前面的原子重复0次或者1次 
+ 前面的原子重复1次或多次
{n} 前面的原子出现了 n 次
{n,} 前面的原子至少出现 n 次
{n,m} 前面的原子出现次数介于 n-m 之间
( ) 分组,需要输出的部分

109 常用通用字符总结

\s  匹配空白字符 
\w  匹配任意字母/数字/下划线 
\W  和小写 w 相反,匹配任意字母/数字/下划线以外的字符
\d  匹配十进制数字
\D  匹配除了十进制数以外的值 
[0-9]  匹配一个0-9之间的数字
[a-z]  匹配小写英文字母
[A-Z]  匹配大写英文字母

110 密码安全检查

密码安全要求:1)要求密码为6到20位; 2)密码只包含英文字母和数字

pat = re.compile(r\'\w{6,20}\') # 这是错误的,因为\w通配符匹配的是字母,数字和下划线,题目要求不能含有下划线
# 使用最稳的方法:\da-zA-Z满足`密码只包含英文字母和数字`
pat = re.compile(r\'[\da-zA-Z]{6,20}\')

选用最保险的fullmatch方法,查看是否整个字符串都匹配:

pat.fullmatch(\'qaz12\') # 返回 None, 长度小于6
pat.fullmatch(\'qaz12wsxedcrfvtgb67890942234343434\') # None 长度大于22
pat.fullmatch(\'qaz_231\') # None 含有下划线
pat.fullmatch(\'n0passw0Rd\')
Out[4]: <re.Match object; span=(0, 10), match=\'n0passw0Rd\'>

111 爬取百度首页标题

import re
from urllib import request

#爬虫爬取百度首页内容
data=request.urlopen("http://www.baidu.com/").read().decode()

#分析网页,确定正则表达式
pat=r\'<title>(.*?)</title>\'

result=re.search(pat,data)
print(result) <re.Match object; span=(1358, 1382), match=\'<title>百度一下,你就知道</title>\'>

result.group() # 百度一下,你就知道

112 批量转化为驼峰格式(Camel)

数据库字段名批量转化为驼峰格式

分析过程

# 用到的正则串讲解
# \s 指匹配: [ \t\n\r\f\v]
# A|B:表示匹配A串或B串
# re.sub(pattern, newchar, string): 
# substitue代替,用newchar字符替代与pattern匹配的字符所有.
# title(): 转化为大写,例子:
# \'Hello world\'.title() # \'Hello World\'
# print(re.sub(r"\s|_|", "", "He llo_worl\td"))
s = re.sub(r"(\s|_|-)+", " ",
           \'some_database_field_name\').title().replace(" ", "")  
#结果: SomeDatabaseFieldName
# 可以看到此时的第一个字符为大写,需要转化为小写
s = s[0].lower()+s[1:]  # 最终结果

整理以上分析得到如下代码:

import re
def camel(s):
    s = re.sub(r"(\s|_|-)+", " ", s).title().replace(" ", "")
    return s[0].lower() + s[1:]

# 批量转化
def batch_camel(slist):
    return [camel(s) for s in slist]

测试结果:

s = batch_camel([\'student_id\', \'student\tname\', \'student-add\'])
print(s)
# 结果
[\'studentId\', \'studentName\', \'studentAdd\']

113 str1是否为str2的permutation

排序词(permutation):两个字符串含有相同字符,但字符顺序不同。

from collections import defaultdict


def is_permutation(str1, str2):
    if str1 is None or str2 is None:
        return False
    if len(str1) != len(str2):
        return False
    unq_s1 = defaultdict(int)
    unq_s2 = defaultdict(int)
    for c1 in str1:
        unq_s1[c1] += 1
    for c2 in str2:
        unq_s2[c2] += 1

    return unq_s1 == unq_s2

这个小例子,使用python内置的defaultdict,默认类型初始化为int,计数默次数都为0. 这个解法本质是 hash map lookup

统计出的两个defaultdict:unq_s1,unq_s2,如果相等,就表明str1、 str2互为排序词。

下面测试:

r = is_permutation(\'nice\', \'cine\')
print(r)  # True

r = is_permutation(\'\', \'\')
print(r)  # True

r = is_permutation(\'\', None)
print(r)  # False

r = is_permutation(\'work\', \'woo\')
print(r)  # False

以上就是使用defaultdict的小例子,希望对读者朋友理解此类型有帮助。

114 str1是否由str2旋转而来

stringbook旋转后得到bookstring,写一段代码验证str1是否为str2旋转得到。

思路

转化为判断:str1是否为str2+str2的子串

def is_rotation(s1: str, s2: str) -> bool:
    if s1 is None or s2 is None:
        return False
    if len(s1) != len(s2):
        return False

    def is_substring(s1: str, s2: str) -> bool:
        return s1 in s2
    return is_substring(s1, s2 + s2)

测试

r = is_rotation(\'stringbook\', \'bookstring\')
print(r)  # True

r = is_rotation(\'greatman\', \'maneatgr\')
print(r)  # False

115 正浮点数

从一系列字符串中,挑选出所有正浮点数。

该怎么办?

玩玩正则表达式,用正则搞它!

关键是,正则表达式该怎么写呢?

有了!

^[1-9]\d*\.\d*$

^ 表示字符串开始

[1-9] 表示数字1,2,3,4,5,6,7,8,9

^[1-9] 连起来表示以数字 1-9 作为开头

\d 表示一位 0-9 的数字

* 表示前一位字符出现 0 次,1 次或多次

\d* 表示数字出现 0 次,1 次或多次

\. 表示小数点

\$ 表示字符串以前一位的字符结束

^[1-9]\d*\.\d*$ 连起来就求出所有大于 1.0 的正浮点数。

那 0.0 到 1.0 之间的正浮点数,怎么求,干嘛不直接汇总到上面的正则表达式中呢?

这样写不行吗:^[0-9]\d*\.\d*$

OK!

那我们立即测试下呗

In [85]: import re

In [87]: recom = re.compile(r\'^[0-9]\d*\.\d*$\')

In [88]: recom.match(\'000.2\')
Out[88]: <re.Match object; span=(0, 5), match=\'000.2\'>

结果显示,正则表达式 ^[0-9]\d*\.\d*$ 竟然匹配到 000.2 ,认为它是一个正浮点数~~~!!!!

晕!!!!!!

所以知道为啥要先匹配大于 1.0 的浮点数了吧!

如果能写出这个正则表达式,再写另一部分就不困难了!

0.0 到 1.0 间的浮点数:^0\.\d*[1-9]\d*$

两个式子连接起来就是最终的结果:

^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$

如果还是看不懂,看看下面的正则分布剖析图吧:

三、Python文件、日期和多线程

Python文件IO操作涉及文件读写操作,获取文件后缀名,修改后缀名,获取文件修改时间,压缩文件,加密文件等操作。

Python日期章节,由表示大日期的calendardate模块,逐渐过渡到表示时间刻度更小的模块:datetimetime模块,按照此逻辑展开。

Python多线程希望透过5个小例子,帮助你对多线程模型编程本质有些更清晰的认识。

一共总结最常用的26个关于文件和时间处理模块的例子。

116 获取后缀名

import os
file_ext = os.path.splitext(\'./data/py/test.py\')
front,ext = file_ext
In [5]: front
Out[5]: \'./data/py/test\'

In [6]: ext
Out[6]: \'.py\'

117 文件读操作

import os
# 创建文件夹

def mkdir(path):
    isexists = os.path.exists(path)
    if not isexists:
        os.mkdir(path)
# 读取文件信息

def openfile(filename):
    f = open(filename)
    fllist = f.read()
    f.close()
    return fllist  # 返回读取内容

118 文件写操作

# 写入文件信息
# example1
# w写入,如果文件存在,则清空内容后写入,不存在则创建
f = open(r"./data/test.txt", "w", encoding="utf-8")
print(f.write("测试文件写入"))
f.close

# example2
# a写入,文件存在,则在文件内容后追加写入,不存在则创建
f = open(r"./data/test.txt", "a", encoding="utf-8")
print(f.write("测试文件写入"))
f.close

# example3
# with关键字系统会自动关闭文件和处理异常
with open(r"./data/test.txt", "w") as f:
    f.write("hello world!")

119 路径中的文件名

In [11]: import os
    ...: file_ext = os.path.split(\'./data/py/test.py\')
    ...: ipath,ifile = file_ext
    ...:

In [12]: ipath
Out[12]: \'./data/py\'

In [13]: ifile
Out[13]: \'test.py\'

120 批量修改文件后缀

批量修改文件后缀

本例子使用Python的os模块和 argparse模块,将工作目录work_dir下所有后缀名为old_ext的文件修改为后缀名为new_ext

通过本例子,大家将会大概清楚argparse模块的主要用法。

导入模块

import argparse
import os

定义脚本参数

def get_parser():
    parser = argparse.ArgumentParser(
        description=\'工作目录中文件后缀名修改\')
    parser.add_argument(\'work_dir\', metavar=\'WORK_DIR\', type=str, nargs=1,
                        help=\'修改后缀名的文件目录\')
    parser.add_argument(\'old_ext\', metavar=\'OLD_EXT\',
                        type=str, nargs=1, help=\'原来的后缀\')
    parser.add_argument(\'new_ext\', metavar=\'NEW_EXT\',
                        type=str, nargs=1, help=\'新的后缀\')
    return parser

后缀名批量修改

def batch_rename(work_dir, old_ext, new_ext):
    """
    传递当前目录,原来后缀名,新的后缀名后,批量重命名后缀
    """
    for filename in os.listdir(work_dir):
        # 获取得到文件后缀
        split_file = os.path.splitext(filename)
        file_ext = split_file[1]
        # 定位后缀名为old_ext 的文件
        if old_ext == file_ext:
            # 修改后文件的完整名称
            newfile = split_file[0] + new_ext
            # 实现重命名操作
            os.rename(
                os.path.join(work_dir, filename),
                os.path.join(work_dir, newfile)
            )
    print("完成重命名")
    print(os.listdir(work_dir))

实现Main

def main():
    """
    main函数
    """
    # 命令行参数
    parser = get_parser()
    args = vars(parser.parse_args())
    # 从命令行参数中依次解析出参数
    work_dir = args[\'work_dir\'][0]
    old_ext = args[\'old_ext\'][0]
    if old_ext[0] != \'.\':
        old_ext = \'.\' + old_ext
    new_ext = args[\'new_ext\'][0]
    if new_ext[0] != \'.\':
        new_ext = \'.\' + new_ext

    batch_rename(work_dir, old_ext, new_ext)

121 xls批量转换成xlsx

import os
def xls_to_xlsx(work_dir): """ 传递当前目录,原来后缀名,新的后缀名后,批量重命名后缀 """ old_ext, new_ext = \'.xls\', \'.xlsx\' for filename in os.listdir(work_dir): # 获取得到文件后缀 split_file = os.path.splitext(filename) file_ext = split_file[1] # 定位后缀名为old_ext 的文件 if old_ext == file_ext: # 修改后文件的完整名称 newfile = split_file[0] + new_ext # 实现重命名操作 os.rename( os.path.join(work_dir, filename), os.path.join(work_dir, newfile) ) print("完成重命名") print(os.listdir(work_dir)) xls_to_xlsx(\'./data\') # 输出结果: # [\'cut_words.csv\', \'email_list.xlsx\', \'email_test.docx\', \'email_test.jpg\', \'email_test.xlsx\',\
\'geo_data.png\', \'geo_data.xlsx\',\'iotest.txt\', \'pyside2.md\', \'PySimpleGUI-4.7.1-py3-none-any.whl\',\
\'test.txt\', \'test_excel.xlsx\', \'ziptest\', \'ziptest.zip\']

122 定制文件不同行

比较两个文件在哪些行内容不同,返回这些行的编号,行号编号从1开始。

定义统计文件行数的函数

# 统计文件个数
    def statLineCnt(statfile):
        print(\'文件名:\'+statfile)
        cnt = 0
        with open(statfile, encoding=\'utf-8\') as f:
            while f.readline():
                cnt += 1
            return cnt

统计文件不同之处的子函数:

# more表示含有更多行数的文件
        def diff(more, cnt, less):
            difflist = []
            with open(less, encoding=\'utf-8\') as l:
                with open(more, encoding=\'utf-8\') as m:
                    lines = l.readlines()
                    for i, line in enumerate(lines):
                        if line.strip() != m.readline().strip():
                            difflist.append(i)
            if cnt - i > 1:
                difflist.extend(range(i + 1, cnt))
            return [no+1 for no in difflist]

主函数:

# 返回的结果行号从1开始
# list表示fileA和fileB不同的行的编号

def file_diff_line_nos(fileA, fileB):
    try:
        cntA = statLineCnt(fileA)
        cntB = statLineCnt(fileB)
        if cntA > cntB:
            return diff(fileA, cntA, fileB)
        return diff(fileB, cntB, fileA)

    except Exception as e:
        print(e)

比较两个文件A和B,拿相对较短的文件去比较,过滤行后的换行符\n和空格。

暂未考虑某个文件最后可能有的多行空行等特殊情况

使用file_diff_line_nos 函数:

if __name__ == \'__main__\':
    import os
    print(os.getcwd())

    \'\'\'
    例子:
    fileA = "\'hello world!!!!\'\'\
            \'nice to meet you\'\
            \'yes\'\
            \'no1\'\
            \'jack\'"
    fileB = "\'hello world!!!!\'\'\
            \'nice to meet you\'\
            \'yes\' "
    \'\'\'
    diff = file_diff_line_nos(\'./testdir/a.txt\', \'./testdir/b.txt\')
    print(diff)  # [4, 5]

关于文件比较的,实际上,在Python中有对应模块difflib , 提供更多其他格式的文件更详细的比较,大家可参考:

https://docs.python.org/3/library/difflib.html?highlight=difflib#module-difflib

123 获取指定后缀名的文件

import os

def find_file(work_dir,extension=\'jpg\'):
    lst = []
    for filename in os.listdir(work_dir):
        print(filename)
        splits = os.path.splitext(filename)
        ext = splits[1] # 拿到扩展名
        if ext == \'.\'+extension:
            lst.append(filename)
    return lst

r = find_file(\'.\',\'md\') 
print(r) # 返回所有目录下的md文件

124 批量获取文件修改时间

# 获取目录下文件的修改时间
import os
from datetime import datetime

print(f"当前时间:{datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\')}")

def get_modify_time(indir):
    for root, _, files in os.walk(indir):  # 循环D:\works目录和子目录
        for file in files:
            absfile = os.path.join(root, file)
            modtime = datetime.fromtimestamp(os.path.getmtime(absfile))
            now = datetime.now()
            difftime = now-modtime
            if difftime.days < 20:  # 条件筛选超过指定时间的文件
                print(f"""{absfile}
                    修改时间[{modtime.strftime(\'%Y-%m-%d %H:%M:%S\')}]
                    距今[{difftime.days:3d}天{difftime.seconds//3600:2d}时{difftime.seconds%3600//60:2d}]"""
                      )  # 打印相关信息


get_modify_time(\'./data\')
打印效果:
当前时间:2019-12-22 16:38:53
./data\cut_words.csv
                    修改时间[2019-12-21 10:34:15]
                    距今[  1天 6时 4]
当前时间:2019-12-22 16:38:53
./data\cut_words.csv
                    修改时间[2019-12-21 10:34:15]
                    距今[  1天 6时 4]
./data\email_test.docx
                    修改时间[2019-12-03 07:46:29]
                    距今[ 19天 8时52]
./data\email_test.jpg
                    修改时间[2019-12-03 07:46:29]
                    距今[ 19天 8时52]
./data\email_test.xlsx
                    修改时间[2019-12-03 07:46:29]
                    距今[ 19天 8时52]
./data\iotest.txt
                    修改时间[2019-12-13 08:23:18]
                    距今[  9天 8时15]
./data\pyside2.md
                    修改时间[2019-12-05 08:17:22]
                    距今[ 17天 8时21]
./data\PySimpleGUI-4.7.1-py3-none-any.whl
                    修改时间[2019-12-05 00:25:47]
                    距今[ 17天16时13]

125 批量压缩文件

import zipfile  # 导入zipfile,这个是用来做压缩和解压的Python模块;
import os
import time

def batch_zip(start_dir):
    start_dir = start_dir  # 要压缩的文件夹路径
    file_news = start_dir + \'.zip\'  # 压缩后文件夹的名字

    z = zipfile.ZipFile(file_news, \'w\', zipfile.ZIP_DEFLATED)
    for dir_path, dir_names, file_names in os.walk(start_dir):
        # 这一句很重要,不replace的话,就从根目录开始复制
        f_path = dir_path.replace(start_dir, \'\')
        f_path = f_path and f_path + os.sep  # 实现当前文件夹以及包含的所有文件的压缩
        for filename in file_names:
            z.write(os.path.join(dir_path, filename), f_path + filename)
    z.close()
    return file_news


batch_zip(\'./data/ziptest\')

126 32位加密

import hashlib
# 对字符串s实现32位加密


def hash_cry32(s):
    m = hashlib.md5()
    m.update((str(s).encode(\'utf-8\')))
    return m.hexdigest()


print(hash_cry32(1))  # c4ca4238a0b923820dcc509a6f75849b
print(hash_cry32(\'hello\'))  # 5d41402abc4b2a76b9719d911017c592

127 年的日历图

import calendar
from datetime import date
mydate = date.today()
year_calendar_str = calendar.calendar(2019)
print(f"{mydate.year}年的日历图:{year_calendar_str}\n")

打印结果:

2019

      January                   February                   March
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    1  2  3  4  5  6                   1  2  3                   1  2  3
 7  8  9 10 11 12 13       4  5  6  7  8  9 10       4  5  6  7  8  9 10
14 15 16 17 18 19 20      11 12 13 14 15 16 17      11 12 13 14 15 16 17
21 22 23 24 25 26 27      18 19 20 21 22 23 24      18 19 20 21 22 23 24
28 29 30 31               25 26 27 28               25 26 27 28 29 30 31

       April                      May                       June
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
 1  2  3  4  5  6  7             1  2  3  4  5                      1  2
 8  9 10 11 12 13 14       6  7  8  9 10 11 12       3  4  5  6  7  8  9
15 16 17 18 19 20 21      13 14 15 16 17 18 19      10 11 12 13 14 15 16
22 23 24 25 26 27 28      20 21 22 23 24 25 26      17 18 19 20 21 22 23
29 30                     27 28 29 30 31            24 25 26 27 28 29 30

        July                     August                  September
Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      

分类:

技术点:

相关文章: