python语法

it2026-08-18  6

https://www.liaoxuefeng.com/wiki/1016959663602400

 

文章目录

@[toc]0、前言1、转义方式2、字符串的占位符 百分号占位符format格式化占位符3、布尔值4、if判断5、除法运算6、list7、tuple8、输入输出9、数字字符串转换10、循环11、字典dict 遍历dict:12、集合set13、函数 可变参数关键字参数命名关键字参数:参数组合函数注释/注解匿名函数lambda装饰器偏函数14、切片15、迭代 迭代字典dict下标循环应用迭代的函数 sortedzipenumeratefiltermapreducesumanyallmaxmin16、列表生成式 列表生成式字典生成式集合生成式17、生成器 使用生成式来编写生成器使用yeild来编写生成器 使用send给生成器函数传送值生成器的return18、迭代器 单个迭代器和多个迭代器19、闭包20、模块 main函数21、类 \_\_slots\_\_property重载运算符枚举类元类 typemetaclass22、异常 断言assertlogpdb单元测试23、IO 文件StringIO, BytesIO文件和目录序列化 picklejson24、进程和线程 进程 forkProcessPool子进程进程间通讯线程 锁线程局部变量25、正则表达式 切分字符串分组编译正则表达式26、常用内建模块 datatimecollectionsbase64structhashlibhmacitertools groupbycontextliburllibXMLHTMLParser27、第三方模块 Pillowrequestschardetpsutiltqdm28、virtualenv29、socket TCPUDP30、SQL SQLAlchemy31、异步IO 协程 并发(concurrency)和并行(parallelism)的区别:协程和线程的区别:协程的生产者和消费者yeild fromasyncio @asyncio.coroutine装饰器async/awaitaiohttpfuture ProcessPoolExecutorasync with/async for

0、前言

本文是根据廖雪峰python教程学习总结而来。 参考《python学习手册》,《流畅的python》以及python官方手册等网络资料 略过了与C和C++ 语言相同的语法部分

1、转义方式

python的字符串可以用双引号和单引号,作用是相同的。转义的方式和C差不多。不同的是,如果想让一个字符串里面的字符都不用转义的话,可以在字符串前面加字母r来声明。 例如

r'\\\\t\t\t\t\' 1

里面的内容都无需转义。 或者使用3个引号来把字符串包起来,这样也不需要转义了,而且中间还可以换行。 例如

'''123\\\t\t\t\''' 1

2、字符串的占位符

百分号占位符

和C语言一样,都是使用百分号占位,但是不同的是,python里面占位符对应的数据也需要百分号来标志,而且占位符对应的数据与字符串之间不需要加逗号。 例如

print ("我叫%s,今年%d岁,工作%d年了" %('郑德伦', 27, 2)) 1

format格式化占位符

还有一种新的占位符使用{0}占位,有点像C#的格式化字符串操作。python官方手册str.format部分

print('{0:,.3f} {1},{name}, {2},'.format(99999.12345, '嘎嘎',[1,2,3,4], name='123')) 1

输出:

99,999.123 嘎嘎,123, [1, 2, 3, 4], 1

3、布尔值

True False表示真假,布尔运算是and or not,这点与C语言不同

4、if判断

与C语言不同的是,if和else后面都加冒号,else if变成了elif

a = 10 b = 20 if a > b : print (a > b) elif a == b: print (a == b) else: print (a < b) 12345678

5、除法运算

C语言中,两个整数相除的结果还是整数,而在python中,结果是一个浮点数。 如果想让两个整数相除的结果变成整数的话,需要用地板除的方式,双斜线 例如:

10//3 1

结果是3

6、list

list有点像是C++ 中的vector,可以动态的添加和删除元素。是一个有序的表。可以对一个list使用len来获取长度。与C++ 不同,C++ 是强类型的语言,vector中只能包含相同类型的元素,而python的list中可以包含不同类型的对象,还可以包含另一个list。 list使用中括号来表示 例如:

['123', '234', '345] 1

通过索引访问list时,还可以使用负数,-1就是取最后一个元素

a=['123', '234', '345'] a[-1] 12

输出345 在后面追加元素,相当于C++ 中vector的push_back,在python中是append 例如

a.append('666') 1

在中间插入元素,相当于C++ 的insert,在python也是insert 例如a.insert(1, ‘777’) 在索引号1的地方插入’777’ 在末尾删除元素,相当于C++ 的pop_back,在python中是pop 例如

a.pop() 1

删除指定索引的元素,在C++ 中无法通过索引直接删除vector的元素,需要使用迭代器,在python中也用pop删除指定位置的元素 例如

a.pop(1) 1

删除索引1的元素

7、tuple

tuple类似于C++ 中的tuple,和python的list也很像,只不过tuple一旦创建就不能更改里面的内容了。 tuple可以隐式转换为单个变量例如:

a, b, c = (1, 2, 3) print(a, b, c) 12

输出:

1 2 3 1

也可以使用封包

first, *rest = (1,2,3,4) print(first) print(rest) 123

输出

1 [2, 3, 4] 12

8、输入输出

输出函数和C语言类似使用

print ("hello world") 1

输入函数与C和C++ 都比较不同,使用input来获取输入的内容 例如

name = input("input your name") 1

9、数字字符串转换

在C和C++ 中数字转字符串比较麻烦,一般在C语言中可以使用sprintf。C++ 直接使用to_string。 字符串转数字的话,C语言可以使用atoi,C++ 可以使用stringstream。 在python中可以直接使用int() float() str()转换 例如:

a = int('123') b = float('12.3) c = str(123.3) 123

10、循环

python中有两种循环,一种是for循环一种是while循环。和C、C++ 中的不太一样。没有do while循环 for循环:类似于C++中的for (auto i : vec)这种range-based的for循环

for x in range(10): print(x) 12

while循环:和C、C++中的while循环类似

while x < 10: print(x) x += 1 123

python的循环中同样可以使用continue和break来终止循环

11、字典dict

字典类似与C++ 中的unordered_map,是一种哈希表的key-value的查找结构。 使用大括号来表示:

score = {"Mike":100, "冰封飞飞":100, "Fvck":88} 1

判断一个key在不在字典里面可以使用 “冰封飞飞” in score 返回结果是True或者False 删除一个元素使用pop方法。score.pop(“冰封飞飞”) 插入一个元素和C++一样直接可以score[“new”] = 100 查找操作可以使用get操作,因为如果key不存在的时候,直接使用score[“nonexist”]会报错。 get操作还可以指定默认值,如果查找不到key的话,返回默认值

s = {} print(s.get('a', '3')) 12

结果:

3 1

遍历dict:

for (k, v) in score.items(): print("%s:%d" % (k, v)) 12

12、集合set

集合类似与C++ 中的unordered_ser,是一种哈希表的集合结构。 如果要创建set,需要提供一个list作为输入集合。 s = set([1, 2, 2, 3]) 初始化时,自动会去重元素。 结果是s = {1, 2, 3} 添加元素使用add方法,删除元素使用remove方法

s.add(4) s.remove(4) 12

集合类似于数学上的集合,有交集和并集的操作, s1 & s2, s1 | s2

13、函数

函数名是一个指向函数对象的引用,下面可以直接将函数赋值一个别名

a = abs a(-10) 12

而且函数名本身也是一个变量,可以赋值

abs = 10 abs(-10) 12

这样执行的话,abs(-10)就会报错了。

python包含了很多内置的函数,可以从python官方手册查看 函数的定义使用def

def myFunc(a, b): return a + b 12

空语句可以使用pass,类似于C/C++ 中的分号 可变参数:在参数前面加一个星号,就变成了可变参数,可以传入任意个数的参数,实际上是自动封装成了一个tuple。

可变参数

def myFunc(*num): result = 0 for i in num: result += i return result 12345

调用时可以myFunc(1, 2, 3)这样调用, 也可以传入一个list或者tuple

param = [1, 2, 3] myFunc(*param) 12

关键字参数

def func(a, b, **kw): print(kw) 12

这个函数里面第三个参数是**kw,实际上是一个字典。 调用时可以这样调用:func(1, 2, name=“123”, age=18) 参数传进去时,实际上a = 1, b = 2 kw = {“name”:“123”, “age”:18} 也可以直接传一个dict作为第三个参数,但是前面需要加两个星号

kw = {"name":"123", "age":18} func(1, 2, **kw) 12

关键字参数必须跟在位置参数后面。下面的调用方式是不合法的。

def func(a, b=1, c=2): print(a, b, c) func(a=1, 2) 123

命名关键字参数:

def func(a, b, *, name, job): print(name) print(job) 123

在上面关键字参数,无法控制**kw传入的key是什么。使用关键字参数可以控制可以传入哪些key。需要使用一个星号作为分隔符。星号后面定义可以传入的key的名称。

func(1, 2, name="1", job=2) -> OK func(1, 2, job = 3) -> ERROR 12

这个星号必不可少,如果正好有一个可变参数,那就不需要额外的星号了 def func(a, b, *args, name, job):也是可以的

参数组合

定义函数的时候可以使用多重参数组合,定义的顺序是,必选参数,默认参数,可变参数,命名关键字参数和关键字参数

def f1(a, b = 0, *args, **kw) def f2(a, b = 0, *, d, **kw) 12

对于任意的函数都可以使用一个tuple和一个dict来组织参数调用。类似于func(*args, **kw)

函数注释/注解

函数注释是3.X新加入的功能,可以给函数每一个参数添加一个注释,以及标记返回值的类型。

def func(a:'spam', b:(1,10),c:float=2.5) -> int: return a + b + c 12

注解实际上是把上面添加的内容,写入到了函数的__annotations__方法中。 可以使用

print(func.__annotations__) 1

打印函数注解,结果如下

{'a': 'spam', 'b': (1, 10), 'c': <class 'float'>, 'return': <class 'int'>} 1

匿名函数lambda

lambda比def功能要小,lambda仅能编写简单的函数,连if也不能使用,也不能写return,返回值就是表达式的结果

f = lambda x, y, z : x + y + z 1

装饰器

装饰器是一种委托的调用方式,将被装饰的函数包装一层,增加一些定制处理。 装饰器的用法如下,@log是一个语法糖,相当于test = log(test),将test函数替换为wrapper函数。 @functools.wraps(func)是系统帮忙做的一些处理,可以让装饰后的函数和原函数看起来一致。例如:包装后的函数__name__还是显示和原函数一致,不会显示为wrapper函数的__name__

import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('<trace {0}: {1}({2})>'.format(time.ctime(), func.__name__, args, kw)) return func(*args, **kw) return wrapper @log def test(a): print('test func:{0}'.format(a)) def main(): test(5) if __name__ == '__main__': main() 123456789101112131415

结果:

<trace Fri Oct 5 22:10:38 2018: test((5,))> test func:5 12

偏函数

偏函数类似与C++ 中的bind,可以将函数的某些参数固定下来,生成一个新的函数。

import functools int2 = functools.partial(int, base=2) print(int2('10101')) 123

结果:

21 1

14、切片

通过切片操作可以很方便的将list,tuple,string中一段对象取出来。python没有提供substr的函数,字符串的取子串的操作都使用切片来进行

L = list(range(10)) print("L = %s " % L) print("L[0:3] = %s " % L[0:3]) print("L[:3]= %s " % L[:3]) print("L[-2:] = %s " % L[-2:]) print("L[-2:-1] = %s " % L[-2:-1]) print("L[:10:2] = %s " % L[:10:2]) print("L[:] = %s " % L[:]) 12345678

结果:

L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] L[0:3] = [0, 1, 2] L[:3]= [0, 1, 2] L[-2:] = [8, 9] L[-2:-1] = [8] L[:10:2] = [0, 2, 4, 6, 8] L[:] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1234567

切片是一个左闭右开的区间,例如L[0:3],获取[0, 3)区间的元素。 第二个冒号之后的数字是步长,可以相隔步长来取数 只包含一个冒号就是获取变量本身

15、迭代

python的迭代使用for,10循环中说过一些for的内容。类似于C++ 的for(auto i : vec)这种形式的迭代。不是基于下标迭代。

迭代字典dict

d = {'a' : 1, 'b' : 2} for key in d: print (key) for value in d.values(): print (value) for k, v in d.items(): print('%s : %d' % (k, v)) 1234567

输出结果:

a b 1 2 a : 1 b : 2 123456

对于字典,for迭代只会迭代key,如果迭代value的话需要使用d.values()作为迭代对象。如果迭代key-value的话,需要使用d.items()作为迭代对象 判断一个变量是否能迭代,使用下面代码判断。

from collections.abc import Iterable d = {'a' : 1, 'b' : 2} print(isinstance(d, Iterable)) print(isinstance(1, Iterable)) 1234

输出

True False 12

下标循环

使用enumerate可以将一个可迭代的对象转换为下标+对象的tuple来迭代

from collections.abc import Iterable d = {'a' : 1, 'b' : 2} for i in enumerate(d.items()): print(i) 12345

输出

(0, ('a', 1)) (1, ('b', 2)) 12

这个迭代器我用的是i一个元素,这时候i就变成了一个tuple,如果使用两个元素的话,会将这个tuple拆分到两个元素上面去 例如改为:

for i, v in enumerate(d.items()): print('%s : %s' % (i, v)) 12

结果就是

0 : ('a', 1) 1 : ('b', 2) 12

应用迭代的函数

sorted

对参数进行排序

L = [3, 2, 4, 1] L = sorted(L) print (L) 123

结果

[1, 2, 3, 4] 1

zip

将参数相同索引的数据组成一个tuple,如果几个参数的数量不同,按最小的算

L = [1, 2, 3, 4] S = ['a', 'b', 'c', 'd', 'e'] Z = zip(L, S) print (list(Z)) 1234

结果

[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')] 1

enumerate

将参数返回一个索引+数据的tuple

S = ['a', 'b', 'c', 'd', 'e'] print (list(enumerate(S))) 12

结果

[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')] 1

filter

将可迭代对象按照参数1,进行过滤。

def isInt(x): if isinstance(x, int): return True else: return False def main(): S = [1, 2, 3.0, 4.1, '5', (6,)] print (list(filter(isInt, S))) if __name__ == "__main__": main() 1234567891011

结果

[1, 2] 1

map

使用参数1中的函数,对后面的参数进行迭代运算,如果后面迭代对象数量不一致,按最小的算。

def add(x, y): return x + y def main(): S = [1, 2, 3.0, 4.1] L = [2, 3, 4, 5, 6] print (list(map(add, S, L))) if __name__ == "__main__": main() 12345678

结果

[3, 5, 7.0, 9.1] 1

reduce

使用参数1中的函数,对后面的参数进行累加运算,得到一个结果。

from functools import reduce def main(): d = reduce(lambda x, y : x + y, range(101)) print(d) if __name__ == "__main__": main() 123456

结果

5050 1

sum

对参数求和

any

对参数全部进行or运算

all

对参数全部进行and运算

max

求参数最大值

min

求参数最小值

16、列表生成式

使用range可以生成一个范围的数据集合,但是range的返回值是Object对象,需要转换为其他对象才可以使用。例如生成一个list

print(list(range(1, 11))) 1

结果为:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 1

列表生成式

L = [ m * n for m in range(1, 10) for n in range(1, 10) if m >= n] print(L) 12

结果为:

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

可以用一句话来使用嵌套循环和条件判断语句以及一个表达式来生成一个list,同样也可以使用相同的方法来生成一个dict,一个set

字典生成式

将列表生成式的中括号换成大括号,然后将表达式换成一个key:value的形式就可以变成字典生成式了。

L = { ('%dx%d' % (m, n)) :(m * n) for m in range(1, 10) for n in range(1, 10) if m <= n} cntLine = 1 for i, v in enumerate(L.items()): print(v[0], '=', v[1], end=" ") if (v[1] % 9 == 0) and (v[1] / 9 == cntLine): print() cntLine += 1 1234567

结果:

1x1 = 1 1x2 = 2 1x3 = 3 1x4 = 4 1x5 = 5 1x6 = 6 1x7 = 7 1x8 = 8 1x9 = 9 2x2 = 4 2x3 = 6 2x4 = 8 2x5 = 10 2x6 = 12 2x7 = 14 2x8 = 16 2x9 = 18 3x3 = 9 3x4 = 12 3x5 = 15 3x6 = 18 3x7 = 21 3x8 = 24 3x9 = 27 4x4 = 16 4x5 = 20 4x6 = 24 4x7 = 28 4x8 = 32 4x9 = 36 5x5 = 25 5x6 = 30 5x7 = 35 5x8 = 40 5x9 = 45 6x6 = 36 6x7 = 42 6x8 = 48 6x9 = 54 7x7 = 49 7x8 = 56 7x9 = 63 8x8 = 64 8x9 = 72 9x9 = 81 123456789

集合生成式

和字典生成式没啥区别,只是把表达式换成一个值就可以了 有列表,字典,集合生成式但是没有tuple生成式,如果想生成一个tuple的话,需要先生成一个list然后使用tuple()转换成一个tuple。因为小括号这个符号被生成器生成式占用了。

17、生成器

使用生成式来编写生成器

将上面的生成式中括号换成小括号就变成了生成式,生成式是一种特殊的函数,使用next操作来取下一次的值,不会将所有值全部计算出来,而是用到的时候再做计算,节省内存。

g = (x * x for x in range(1, 10)) print (next(g)) print (next(g)) print (next(g)) print (next(g)) 12345

输出:

1 4 9 16 1234

同时,生成式也是一个可迭代的对象,可以使用for来迭代

g = (x * x for x in range(1, 10)) for i in g: print (i) 123

输出:

1 4 9 16 25 36 49 64 81 123456789

使用yeild来编写生成器

yeild的字典含义是产出和让步,对于python来说,这两个含义都成立 yeild b,这个语句会产出一个值,给next(…)的调用方,此外还会做出让步,暂停执行生成器,让调用方继续工作。

def fib(num): cnt, b, c = 0, 0, 1 while cnt < num: yield b b, c = c, b + c cnt += 1 print('done') f = fib(6) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) 123456789101112131415

结果:

0 1 1 2 3 5 123456

yeild类似于一个不完全的return,可以保存状态。每次到yeild时,程序会返回,然后使用next执行的话,可以从本次yeild的位置继续执行

使用send给生成器函数传送值

yeild是给调用者返回一个值,而send是调用者给函数发送一个值。x = yeild i这种情况下,使用send(N)之后,x就被赋值成了N。send必须在调用完至少一次next操作时,才可调用。要确保函数的语句执行到yeild处。

def test(): for i in range(10): x = yield i ** 2 print("x={0}".format(x)) def main(): G = test() print(next(G)) print(G.send(5)) print(next(G)) print(next(G)) 12345678910

结果:

0 x=5 1 x=None 4 x=None 9 1234567

生成器的return

在生成器中的return the_result语句会抛出StopIteration(the_result)异常,这样调用方可以用异常的value属性中获取the_result。

18、迭代器

可以被for循环迭代的对象都叫做可迭代对象,可以使用isinstance Iterfable来判断对象是否可以被for循环迭代

from collections.abc import Iterable isinstance(obj, Iterable) 12

除了被for循环迭代,还有一些对象可以使用next迭代。可以使用isinstance Iteraotr来判断是否可以被next迭代

from collections.abc import Iterator isinstance(obj, Iterator) 12

使用iter()可以将list,dict等Iterable对象变成Iterator的

L = list(range(10)) Iter = iter(L) print (next(Iter)) print (next(Iter)) 1234

单个迭代器和多个迭代器

有些对象支持单个迭代器,有些支持多个迭代器。单个迭代器是指,使用iter获取该对象的迭代器时,如同单例模式一样,多次获取都是获取同一个迭代器。迭代器1增加,迭代器2也会同时增加 例如range()支持多个迭代器,内置的list等也支持多个迭代器:

R = range(3) R = range(3) I1 = iter(R) I2 = iter(R) print(next(I1)) print(next(I2)) print(next(I2)) 1234567

结果:

0 0 1 123

zip,map,filter,生成器不支持多个迭代器

Z = zip((1, 2, 3), (10, 11, 12)) I1 = iter(Z) I2 = iter(Z) print(next(I1)) print(next(I1)) print(next(I2)) 123456

结果:

(1, 10) (2, 11) (3, 12) 123

19、闭包

闭包的数学含义是能够读取其他函数变量的函数。在python中在一个函数的内部定义另外一个函数,才可以访问这个函数内部的变量,所以在python中闭包可以理解为定义在函数内部的函数。

def createCounter(): ''' 利用闭包返回一个计数器函数,每次调用它返回递增整数: ''' num = 0 def counter(): nonlocal num num += 1 return num return counter 12345678910

20、模块

模块通常是一个文件,在python中,模块也是一个对象。模块内的变量,函数,类都是模块的属性。 python文件开头很常见的两个注释

#!/usr/bin/env python3 # -*- coding: utf-8 -*- 12

main函数

在python中,一个模块如果是被python直接加载的话,__name__属性为’main’,如果是被其他模块加载的话,__name__就是模块的名称。python一般使用下面的代码作为main函数

if __name__=='__main__': test() 12

21、类

python3的所有类都是继承自object基类的,也支持多重继承。 python中的类和C++中的类有点区别,python中的类也是一种对象,类的实例也是一个对象。 python中也没有私有属性的概念,需要用一些其他手法来实现。在属性名前面加双下划线是一种伪私有的概念,因为他把属性的名称替换成了_类名__属性名这种表示形式。

class A(object): def __init__(self): self.name = 'A' class C(object): def __init__(self): self.color = 'Red' self.__private = 'C' class B(A): def __init__(self): self.age = 50 A.__init__(self) C.__init__(self) def __str__(self): result = '' for (k,v) in self.__dict__.items(): result += '{0}={1}\r\n'.format(k, v) return result def main(): a = B() print(a) if __name__ == '__main__': main() 1234567891011121314151617181920212223

结果:

age=50 name=A color=Red _C__private=C 1234

__slots__

由于python是动态类型的语言,类的属性可以使用代码在运行过程中动态的添加。如果想要限制用户添加的话,需要使用__slots__来实现。

class test: __slots__ = ['age', 'name'] def __init__(self): self.test = 10 1234

结果:

File "/media/bingfengfeifei/数据/PyCode/helloworld.py", line 37, in __init__ self.test = 10 AttributeError: 'test' object has no attribute 'test' 123

实际上这种做法是把类的__dict__属性删除掉来实现的,动态添加和删除属性就是通过对类的__dict__属性来操作实现。

property

property是将类内的属性字段,包装成setter,getter函数处理,可以增加一些检查和限制。有两种定义属性的方式

class newprops: def getage(self): return self._age def setage(self, value): self._age = value def delage(self): del self._age def __init__(self): self._age = 0 age = property(getage, setage, delage, "help age") #get set del doc class newprops2: @property def age(self): return self._age @age.setter def age(self, value): self._age = value @age.deleter def age(self): del self._age def __init__(self): self._age = 0 def main(): a = newprops() a.age = 20 del(a.age) print(hasattr(a, 'age')) print(help(a)) if __name__ == '__main__': main() 1234567891011121314151617181920212223242526272829303132

结果:

False Help on newprops in module __main__ object: class newprops(builtins.object) | Methods defined here: | | __init__(self) | Initialize self. See help(type(self)) for accurate signature. | | delage(self) | | getage(self) | | setage(self, value) | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | age | help age None 12345678910111213141516171819202122232425262728

重载运算符

python中类的很多操作,都是重定向到一个双下划线开头和结尾的函数__X__,我们重定义这个函数之后,就可以拦截一些内置的操作。

方法重载调用__init__构造函数对象建立时__del__析构函数对象删除时__add__运算符+X+Y,X+=Y(如果没有__iadd__)__or__位或运算符X|Y X|=Y__repr__, __str__打印,转换print(X), repr(X), str(X)__call__函数调用X()__getattr__点号运算X.undefined__setattr__属性赋值语句X.any = value__delattr__属性删除del X.any__getattribute__属性获取X.any__getitem__索引运算X[key],X[i:j]__setitem__索引赋值语句X[key]=value__delitem__索引和分片删除del X[key]__len__长度len(X)__bool__布尔测试bool(X)__lt__,__gt__,__le__,__ge__,__eq__,__ne__特定的比较X < Y, X > Y, X <= Y等__radd__右侧加法Other+X__iadd__原地加法X+=Y__iter__,__next__迭代环境I=Iter(X), next(I)__contains__成员关系测试item in X__index__整数值hex(X), bin(X)__enter__,__exit__环境管理器with obj as var:__get__,__set__,__delete__属性描述符x.attr,x.attr=value,del x.attr__new__创建在__init__之间创建对象

枚举类

from enum import Enum, unique @unique class WeekDay(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 def main(): print(WeekDay['Mon']) print(WeekDay.Sat) print(WeekDay(2)) print('------------') for k,v in WeekDay.__members__.items(): print(k,v,v.value) 1234567891011121314151617

结果:

WeekDay.Mon WeekDay.Sat WeekDay.Tue ------------ Sun WeekDay.Sun 0 Mon WeekDay.Mon 1 Tue WeekDay.Tue 2 Wed WeekDay.Wed 3 Thu WeekDay.Thu 4 Fri WeekDay.Fri 5 Sat WeekDay.Sat 6 1234567891011

元类

元类是可以生成类的类。使用元类可以在运行时动态的创建一个类。元类需要继承type类型。

type

type可以查看一个变量的类型,type也可以动态的创建一个类。 参数1:类名 参数2:基类 参数3:方法dict

Hello = type('Hello', (object,), dict(hello=lambda self : print('hello world'))) a = Hello() a.hello() 123

结果:

hello world 1

metaclass

除了使用type创建类之外,还可以使用元类metaclass来创建。metaclass实际上还是使用type去创建一个新的类。

class myMetaClass(type): def __new__(cls, name, bases, attrs): attrs['test'] = lambda self, value: print(value) return type.__new__(cls, name, bases, attrs) class myClass(metaclass=myMetaClass): pass def main(): a = myClass() a.test([1,2]) 12345678910

结果:

[1, 2] 1

22、异常

由于python是动态语音,python里面任何错误都是通过异常来体现的,包括运行时的错误,“编译错误“等。 python所有的异常都是从BaseException类继承的,但是有一些是系统退出,用户键盘终端等与程序运行无关的异常,也是BaseException继承的。为了让用户可以更准确的捕获python的运行异常,通常只需要捕获Exception异常即可。我们自定义异常时,一般也是继承Exception类 一个异常的例子: else分支是try没有捕获到异常执行,finally分支是最终都要执行的

class MyException(Exception): def __str__(self): return 'my Exception' def main(): try: raise MyException except Exception as e: print (e) else: print('else') finally: print('finally') if __name__ == '__main__': main() 12345678910111213141516

结果:

my Exception finally 12

异常如果没有被捕获,会一直向上抛出,最终python系统会打印一个错误的调用栈。

断言assert

python中的断言也是一种异常,可以看做是一种条件异常,使用assert去判断用户的输入,而不要使用assert去判断系统的错误,系统的错误由异常来处理。

assert False, 'hello' 1

log

python的logging模块分为debug,info,warning,error等几个级别。log级别的顺序也是按这个顺序从低到高。如果开启了某一个级别的log,这个级别及以上的log都会显示。例如开启了debug级别,所有级别的log都会显示。如果开启了warning级别,warning,error级别的会显示。默认是warning级别。

import logging logging.basicConfig(level=logging.INFO) def main(): logging.info('info level') logging.warning('warning level') logging.debug('debug level') logging.error('error level') if __name__ == '__main__': main() 1234567891011

结果:

INFO:root:info level WARNING:root:warning level ERROR:root:error level 123

pdb

使用pdb可以单步调试,使用下面的命令可以使用pdb调试

python3 -m pdb helloworld.py 12

也可以在代码中,使用下面代码加入断点

import pdb pdb.set_trace() 12

详细的pdb使用参考python官方手册pdb部分

单元测试

单元测试使用unittest模块,首先编写一个测试类,继承自unittest.TestCase,然后编写各种测试方法。setUp方法可以在每个测试例开始时执行,tearDown可以在每个测试例结束的时候执行。 下面是一个单元测试的例子

import unittest class Student(object): def __init__(self, name, score): self.name = name self.score = score def get_grade(self): if self.score >= 60 and self.score < 80: return 'B' elif self.score >= 80 and self.score <= 100: return 'A' elif self.score >=0 and self.score < 60: return 'C' else: raise ValueError class TestStudent(unittest.TestCase): def setUp(self): print('set up') def tearDown(self): print('tear down') def test_80_to_100(self): print('start test_80_to_100') s1 = Student('Bart', 80) s2 = Student('Lisa', 100) self.assertEqual(s1.get_grade(), 'A') self.assertEqual(s2.get_grade(), 'A') print('end test_80_to_100') def test_60_to_80(self): print('start test_60_to_80') s1 = Student('Bart', 60) s2 = Student('Lisa', 79) self.assertEqual(s1.get_grade(), 'B') self.assertEqual(s2.get_grade(), 'B') print('end test_60_to_80') def test_0_to_60(self): print('start test_0_to_60') s1 = Student('Bart', 0) s2 = Student('Lisa', 59) self.assertEqual(s1.get_grade(), 'C') self.assertEqual(s2.get_grade(), 'C') print('end test_0_to_60') def test_invalid(self): print('start test_invalid') s1 = Student('Bart', -1) s2 = Student('Lisa', 101) with self.assertRaises(ValueError): s1.get_grade() with self.assertRaises(ValueError): s2.get_grade() print('end test_invalid') def main(): unittest.main() if __name__ == '__main__': main() 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657

结果:

set up start test_0_to_60 end test_0_to_60 tear down .set up start test_60_to_80 end test_60_to_80 tear down .set up start test_80_to_100 end test_80_to_100 tear down .set up start test_invalid end test_invalid tear down . ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK 12345678910111213141516171819202122

执行测试例有两个方法:一个是使用下面的代码执行

python3 -m unittest helloworld 1

还有一种是像上面的测试代码一样,在main函数里面执行unittest.main()。这种方式可以像执行普通文件一样,直接执行:

python3 helloworld.py 1

但是此方法在我的vscode中不能直接运行,需要从命令行输入上面的命令去运行。

23、IO

文件

python的文件操作接口和posix接口类似,打开文本文件时,还可以通过encoding参数选择编码,也可以通过errors参数忽略编码的错误,文件操作的示例:

def main(): with open('test.txt', 'w', encoding='gbk') as f: f.write('测试gbk\r\n') f.write('hello world\r\n') f.write('哈哈哈\r\n') with open('test.txt', 'r', errors='ignore') as f: for line in f.readlines(): print(line, end='') 12345678

输出: 由于python3默认是utf8打开的,所以这里使用gbk编码的文件打开会出现错误,忽略编码错误之后,中文编码部分没有被读取出来

gbk hello world 12

使用正确的编码测试:

def main(): with open('test.txt', 'w', encoding='gbk') as f: f.write('测试gbk\r\n') f.write('hello world\r\n') f.write('哈哈哈\r\n') with open('test.txt', 'r', encoding='gbk') as f: for line in f.readlines(): print(line, end='') 12345678

输出:

测试gbk hello world 哈哈哈 123

StringIO, BytesIO

IO操作的函数不仅可以用在文件上面,也可以用在很多IO对象上面。StringIO和BytesIO可以创建IO对象。

from io import StringIO from io import BytesIO def main(): f = StringIO('test\r\nhello world\r\n哈哈哈\r\n') for i in f: print(i, end='') print('-------------------') f = StringIO() f.write('new\r\n') f.write('test\r\n') print(f.getvalue()) print('-------------------') f = BytesIO('测试'.encode('utf-8')) for i in f: print(i) 123456789101112131415

输出:

test hello world 哈哈哈 ------------------- new test ------------------- b'\xe6\xb5\x8b\xe8\xaf\x95' 123456789

文件和目录

python的os模块包含了一些操作系统相关的操作。 获取环境变量

>>> os.environ.get('PATH') '/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin' 123

获取当前目录

>>> os.path.abspath('.') '/media/bingfengfeifei/数据/PyCode' 123

连接目录,因为不同操作系统的目录分隔符不一样,所以需要使用python的目录连接来保证各个操作系统都可以生效

>>> a = os.path.join(os.path.abspath('.'),'new') >>> os.mkdir(a) >>> os.listdir('.') ['.vscode', 'helloworld.py', 'new', 'test.py', 'test.txt', '__pycache__'] >>> os.rmdir(a) >>> os.listdir('.') ['.vscode', 'helloworld.py', 'test.py', 'test.txt', '__pycache__'] >>> 123456789

分割目录

>>> os.path.split('/media/bingfengfeifei/数据/PyCode/helloworld.py') ('/media/bingfengfeifei/数据/PyCode', 'helloworld.py') 123

分割扩展名

>>> os.path.splitext('/media/bingfengfeifei/数据/PyCode/helloworld.py') ('/media/bingfengfeifei/数据/PyCode/helloworld', '.py') 12

重命名

>>> os.mkdir('new') >>> os.listdir('.') ['.vscode', 'helloworld.py', 'new', 'test.py', 'test.txt', '__pycache__'] >>> os.rename('new', 'newnew') >>> os.listdir('.') ['.vscode', 'helloworld.py', 'newnew', 'test.py', 'test.txt', '__pycache__'] 123456

删除文件

>>> os.listdir('.') ['.vscode', 'helloworld.py', 'newnew', 'test.py', 'test.txt', '__pycache__'] >>> os.remove('test.txt') >>> os.listdir('.') ['.vscode', 'helloworld.py', 'newnew', 'test.py', '__pycache__'] >>> 1234567

shutil模块还补充了一些os模块里面没有的系统操作。例如复制文件

>>> shutil.copyfile('helloworld.py', 'new.py') 1

序列化

pickle

pickle可以将内存中的数据结构,序列化成一个byte array,也可以将一个序列化好的byte array转换成一个对象。

import pickle def main(): d = {'name':'蛤蛤', 'age':-1, 'test':[1,2,3,4]} with open('dump.dat', 'wb') as f: pickle.dump(d, f) with open('dump.dat', 'rb') as f: newDict = pickle.load(f) print(newDict) if __name__ == '__main__': main() 12345678910111213

输出:

{'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4]} 1

dump和load方法是将序列化好的东西向IO对象操作。dumps和loads操作是向byte array操作:

import pickle def main(): d = {'name':'蛤蛤', 'age':-1, 'test':[1,2,3,4]} s = pickle.dumps(d) print(s) newDict = pickle.loads(s) print(newDict) if __name__ == '__main__': main() 123456789101112

输出:

b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00\xe8\x9b\xa4\xe8\x9b\xa4q\x02X\x03\x00\x00\x00ageq\x03J\xff\xff\xff\xffX\x04\x00\x00\x00testq\x04]q\x05(K\x01K\x02K\x03K\x04eu.' {'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4]} 12

json

json模块可以将python中的数据结构和json结构来进行转换。同样可以使用dumps和dump两种方法,和pickle类似

import json def main(): d = {'name':'蛤蛤', 'age':-1, 'test':[1,2,3,4], 'job':None, 'alive':True} a = json.dumps(d) print(a) newDict = json.loads(a) print(newDict) if __name__ == '__main__': main() 123456789101112131415

输出:

{"name": "\u86e4\u86e4", "age": -1, "test": [1, 2, 3, 4], "job": null, "alive": true} {'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4], 'job': None, 'alive': True} 12

json还可以用来序列化class,需要自定义一个dumps中的default函数,用来从class转换为字典,还需要自定义一个loads里面的object_hook函数,用来从字典转换成对象。

class JsonTest: def __init__(self, name = '蛤蛤', age=-1, test=[1,2,3,4], job=None, alive=True): self.name = name self.age = age self.test = test self.job = job self.alive = alive import json def main(): d = JsonTest() a = json.dumps(d,default=lambda x : x.__dict__) print(a) newDict = json.loads(a, object_hook=lambda d : JsonTest(d['name'], d['age'] + 1, d['test'], d['job'], d['alive'])) print(newDict.__dict__) 1234567891011121314

输出:

{"name": "\u86e4\u86e4", "age": -1, "test": [1, 2, 3, 4], "job": null, "alive": true} {'name': '蛤蛤', 'age': 0, 'test': [1, 2, 3, 4], 'job': None, 'alive': True} 12

24、进程和线程

进程

详细内容参考廖雪峰python教程-多进程

fork

在×Nix系统下面可以使用fork来创建子进程,Windows下面没有这个系统调用。

Process

也可以使用multiprocessing.Process来在各个平台创建子进程,这个更为通用

Pool

如果创建的进程数量过多可以使用multiprocessing.Pool进程池来创建多个进程。

子进程

可以使用subprocess模块来启动一个子进程

import subprocess def main(): print('$ ping www.python.org') r = subprocess.call(['ping', 'www.python.org', '-c', '5']) print('Exit code:', r) 1234567

结果:

$ ping www.python.org PING dualstack.python.map.fastly.net (151.101.192.223) 56(84) bytes of data. 64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=1 ttl=51 time=234 ms 64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=2 ttl=51 time=238 ms 64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=3 ttl=51 time=231 ms 64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=4 ttl=51 time=229 ms 64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=5 ttl=51 time=225 ms --- dualstack.python.map.fastly.net ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4003ms rtt min/avg/max/mdev = 225.711/231.863/238.158/4.302 ms Exit code: 0 123456789101112

如果子进程是需要输入内容的,还可以使用communicate()方法来输入内容

import subprocess def main(): print('$ import this') p = subprocess.Popen(['python3'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p.communicate('import this'.encode('utf-8')) print(output.decode('utf-8')) print('Exit code:', p.returncode) if __name__ == '__main__': main() 12345678910111213

输出:

$ import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! Exit code: 0 123456789101112131415161718192021222324

进程间通讯

使用Queue来通信,实现一个生产者消费者模型:

from multiprocessing import Process, Queue def producer(q): print('process producer pid({0})'.format(os.getpid())) for i in ['A', 'B', 'C', 'D', 'E']: print('put value {0}'.format(i)) q.put(i) time.sleep(random.random() * 3) def consumer(q): print('process consumer pid({0})'.format(os.getpid())) while True: print('start value') value = q.get(True) print('get value {0}'.format(value)) def main(): q = Queue() pro = Process(target=producer, args=(q,)) con = Process(target=consumer, args=(q,)) pro.start() con.start() pro.join() con.terminate() 123456789101112131415161718192021222324

输出:

process producer pid(16750) put value A process consumer pid(16751) start value get value A start value put value B get value B start value put value C get value C start value put value D get value D start value put value E get value E start value 123456789101112131415161718

线程

python中的线程使用threading模块

import threading import time def loop(): print('thread {0} is starting.'.format(threading.current_thread().name)) for i in range(5): print('thread {0} loop {1}.'.format(threading.current_thread().name, i)) time.sleep(1) print('thread {0} end.'.format(threading.current_thread().name)) def main(): t = threading.Thread(target=loop,name='test') t2 = threading.Thread(target=loop,name='test2') t.start() t2.start() t.join() t2.join() 123456789101112131415

输出:

thread test is starting. thread test2 is starting. thread test loop 0. thread test2 loop 0. thread test loop 1. thread test2 loop 1. thread test2 loop 2. thread test loop 2. thread test loop 3. thread test2 loop 3. thread test loop 4. thread test2 loop 4. thread test end. thread test2 end. 1234567891011121314

python中的锁,使用threading.Lock()创建,acquire方法来获取锁,release方法来释放锁

lock = threading.Lock() def loop(): lock.acquire() try: print('thread {0} is starting.'.format(threading.current_thread().name)) for i in range(5): print('thread {0} loop {1}.'.format(threading.current_thread().name, i)) time.sleep(1) print('thread {0} end.'.format(threading.current_thread().name)) finally: lock.release() 123456789101112

CPython解释器有一个GIL锁(Global Interpreter Lock),每个python线程执行前都要先获取GIL,然后每执行100条指令释放锁(python3版本改变成了每隔一段时间释放锁),以便于其他线程执行。所以CPython解释器的线程并不能实现真正的并发。

线程局部变量

每个线程里面可以共享全局变量,使用线程ThreadLocal来实现,使用threading.local()来创建ThreadLocal对象

import threading import time thread_local = threading.local() def loop(): thread_local.num = [x for x in range(10)] start() def start(): a = thread_local.num for i in a: print('thread {0} : {1}'.format(threading.current_thread().name, i)) def main(): t = [] for i in range(multiprocessing.cpu_count()): thread = threading.Thread(target=loop) t.append(thread) thread.start() for i in t: i.join() if __name__ == '__main__': main() 123456789101112131415161718192021222324

25、正则表达式

贪婪模式:如果一个正则表达式可以匹配出多个结果,他会尽量多的匹配字符 非贪婪模式:尽量少的匹配字符

表达式作用.匹配任意字符除了换行符^匹配字符串的开头$匹配字符串的结尾+表示至少一个字符*表示任意个字符?表示一个或0个字符*?, +?, ??正则表达式默认是贪婪匹配,在匹配符后面加一个问号开启非贪婪的模式{m}匹配确定m个字符{m,n}匹配m到n个字符{m,n}?匹配m到n个字符的非贪婪模式\转义字符[]匹配中括号中执行的字符,例如[a-z],[0-9A-Z][^5]匹配除了5的所有字符A|B匹配A或者B(…)匹配括号里面的表达式,并且创建一个group\number匹配第number个group的内容 (\d{3})A\1可以匹配’123A123’\A只在字符串的开头匹配,需要配合其他表达式使用\b匹配单词的开头或者结尾,需要配合其他匹配使用\B匹配非单词的开头或者结尾与\b相对\d匹配数字\D匹配非数字\s匹配空白符\S匹配非空白符\w匹配字母数字下划线\W匹配字母数字下划线之外的字符\Z只在字符串的尾部匹配,需要配合其他表达式使用

切分字符串

s = r'123 A1 2 3fd s a f daA123A321Af' a = re.split('\s+', s) print(a) 123

输出:

['123', 'A1', '2', '3fd', 's', 'a', 'f', 'daA123A321Af'] 1

分组

s = r'123 A1 2 3fd s a f daA123A321Af' a = re.match(r'(\A.)(\d{2}).+([a-zA-Z]{2}).+(.\Z)', s) print(a.groups()) 123

输出:

('1', '23', 'aA', 'f') 1

编译正则表达式

s1 = r'xszhengdelun@126.com' s2 = r'sa614374@mail.ustc.edu.cn' pattern = re.compile(r'(.+)@(.+)') print(pattern.match(s1).groups()) print(pattern.match(s2).groups()) 12345

输出:

('xszhengdelun', '126.com') ('sa614374', 'mail.ustc.edu.cn') 12

26、常用内建模块

datatime

from datetime import datetime def main(): print(datetime.now()) dt = datetime(2017, 3, 16, 0, 0) print(dt) print(datetime.now().timestamp()) print(datetime.fromtimestamp(1429417200.0)) print(datetime.strptime('2018-10-05 18:55:22', '%Y-%m-%d %H:%M:%S')) print(datetime.now().strftime('%a, %b %d %H:%M')) if __name__ == '__main__': main() 123456789101112

输出:

2018-10-07 21:41:19.897650 2017-03-16 00:00:00 1538919679.897687 2015-04-19 12:20:00 2018-10-05 18:55:22 Sun, Oct 07 21:41 123456

collections

from collections import namedtuple from collections import deque from collections import defaultdict from collections import OrderedDict from collections import Counter def main(): Point = namedtuple('Point', ['x', 'y']) p = Point(1, 2) print('p =',p) deq = deque(['a', 'b', 'c']) deq.append('d') deq.appendleft('z') print('deq =',deq) deq.pop() deq.popleft() print('deq =',deq) dd = defaultdict(lambda : 'N/A') dd['A'] = 1 dd['B'] = 2 print(dd['C']) od = OrderedDict({'a':1, 'b':2, 'c':3}) #有序dict print('od =',od) c = Counter() for i in 'hello world!': c[i] += 1 print('c = ', c) if __name__ == '__main__': main() 12345678910111213141516171819202122232425262728293031323334

输出:

p = Point(x=1, y=2) deq = deque(['z', 'a', 'b', 'c', 'd']) deq = deque(['a', 'b', 'c']) N/A od = OrderedDict([('a', 1), ('b', 2), ('c', 3)]) c = Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1}) 123456

base64

base64是一种二进制编码方式,采用64个字符来表示任意的二进制数据

import base64 def main(): a = base64.b64encode('冰封飞飞'.encode('utf-8')) print(a) print(base64.b64decode(a).decode('utf-8')) a = base64.urlsafe_b64encode(b'test-url-safe') print(a) print(base64.urlsafe_b64decode(a)) if __name__ == '__main__': main() 12345678910111213

输出:

b'5Yaw5bCB6aOe6aOe' 冰封飞飞 b'dGVzdC11cmwtc2FmZQ==' b'test-url-safe' 1234

struct

struct模块可以处理字节相关的数据。详细内容参考python官方手册struct

字符字节序尺寸对齐<小端序标准无>大端序标准无!网络序标准无@主机序nativenative=主机序标准无 格式C语言类型Python 类型标准尺寸x填充类型no value cchar长度为1的bytes1bsigned charinteger1Bunsigned charinteger1?_Boolbool1hshortinteger2Hunsigned shortinteger2iintinteger4Iunsigned intinteger4llonginteger4Lunsigned longinteger4qlong longinteger8Qunsigned long longinteger8nssize_tinteger Nsize_tinteger e float2ffloatfloat4ddoublefloat8schar[]bytes pchar[]bytes Pvoid*integer  import struct def main(): a = struct.pack('=hHQ',-100, 100,100000000000) print(a.hex()) if __name__ == '__main__': main() 12345678

输出:

9cff640000e8764817000000 1

hashlib

import hashlib def main(): s = '测试MD5哈希摘要算法' md5 = hashlib.md5() md5B = hashlib.md5() md5.update(s.encode('utf-8')) for i in s: md5B.update(i.encode('utf-8')) print(md5.hexdigest()) print(md5B.hexdigest()) sha256 = hashlib.sha256() sha256B = hashlib.sha256() sha256.update(s.encode('utf-8')) for i in s: sha256B.update(i.encode('utf-8')) print(sha256.hexdigest()) print(sha256B.hexdigest()) 12345678910111213141516171819

输出:

725b5fca27c243ab4e61b02b0b74b6e2 725b5fca27c243ab4e61b02b0b74b6e2 bc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdc bc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdc 1234

hmac

import hmac def main(): a = hmac.new(b'test', '测试HMAC'.encode('utf-8'), digestmod='sha256') print(a.hexdigest()) 12345

输出:

4599b96ad2c95e565f4f8d29c925a3eb0d3549b0b913d996b18393a02a03ea42 1

itertools

import itertools def main(): a = itertools.count(1) for i in a: print(i) # 1,2,3,4,5,6,7...无限打印 a = itertools.cycle('ABCD') for i in a: print(i) # A B C D A B C D 循环无限打印 a = itertools.repeat('A', 3) for i in a: print(i) # A A A 打印3次 a = itertools.count(1) b = itertools.takewhile(lambda x : x <= 10, a) for i in b: print(i) #打印 1 2 3 4 5 6 7 8 9 10 for i in itertools.chain('ABC', 'XYZ'): print(i) #打印 A B C X Y Z 连接多个迭代对象 12345678910111213141516171819202122

groupby

groupby将相邻的重复元素挑选出来放在一组

import itertools def main(): a = itertools.groupby('AAabBbBcCAaa', lambda c : c.upper()) for k, group in a: print(k, list(group)) 123456

输出:

A ['A', 'A', 'a'] B ['b', 'B', 'b', 'B'] C ['c', 'C'] A ['A', 'a', 'a'] 1234

contextlib

使用with可以让open语句在离开with时,自动调用close

with open('test.txt', 'r') as f: f.read() 12

我们可以通过重载__enter__和__exit__来实现上下文管理。

class myContext: def __enter__(self): print('enter') def __exit__(self, exc_type, exc_value, traceback): if exc_type: print('Error') else: print('End') def main(): with myContext() as f: pass 123456789101112

输出:

enter End 12

也可以使用@contextmanager装饰器来定义

from contextlib import contextmanager class myContext: pass @contextmanager def context_func(): print('begin') f = myContext() yield f print('end') def main(): with context_func() as f: pass if __name__ == '__main__': main() 123456789101112131415

输出:

begin end 12

还可以使用@contextmanager实现在调用前后自动执行语句

@contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) with tag("h1"): print("hello") print("world") 123456789

输出:

<h1> hello world </h1> 1234

使用closing函数可以将任意对象转换为可以在with里面使用的对象。closing定义如下,实际上就是在执行完之后调用close方法。如果转换的对象没有close方法,就GG了。

@contextmanager def closing(thing): try: yield thing finally: thing.close() 123456

urllib

用于操作URL

XML

from xml.parsers.expat import ParserCreate 1

HTMLParser

from html.parser import HTMLParser 1

27、第三方模块

Pillow

PIL是python2的图形处理库,Pillow是将PIL适配到python3的版本

requests

requests库是处理URL的第三方库,使用起来比urllib要简单

chardet

chardet可以预测bytes的编码,应用在TCP章节

psutil

获取系统信息的工具,可以获取CPU,内存,磁盘,网络,进程

tqdm

展示命令行进度条

28、virtualenv

virtualenv可以隔离python运行环境,解决不同版本python之间的冲突问题

29、socket

TCP

python的socket使用思路和posix接口类似,只不过参数的传入要简单,因为没有类型的概念,所以记忆量要小很多,基本可以直接裸写。linux下C语言的socket裸写压力还有有点大。 Server代码:

# -*- coding: utf-8 -*- import socket import chardet import threading def handlePacket(sock, addr): print(sock) print(addr) while True: data = sock.recv(1024) encoding = chardet.detect(data)['encoding'] data = data.decode(encoding) print(data) if not data or data[:4] == 'exit': break data = 'Hello :' + data sock.send(data.encode(encoding)) sock.close() def main(): with socket.socket() as s: s.bind(('localhost', 6666)) s.listen() while True: sock, addr = s.accept() handleThread = threading.Thread(target=handlePacket,args=(sock, addr)) handleThread.start() if __name__ == '__main__': main() 123456789101112131415161718192021222324252627282930

Client代码:

# -*- coding: utf-8 -*- import time import socket def main(): buffer = [] with socket.socket() as s: data = ('MyClient', 'Test', "Python", 'exit', 'after') s.connect(('localhost', 6666)) for i in data: s.send(i.encode('utf-8')) recvData = s.recv(1024) print(recvData.decode('utf-8')) if __name__ == '__main__': main() 12345678910111213141516

Client也可以直接使用nc来连接

bingfengfeifei@bingfengfeifei-PC:nc localhost 6666 1

UDP

udp的代码写了一个客户端和服务器ping pang报文的测试 Server:

# -*- coding: utf-8 -*- import socket import chardet import threading def main(): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.bind(('localhost', 6666)) while True: data, addr = s.recvfrom(1024) print(data) s.sendto(data, addr) if __name__ == '__main__': main() 123456789101112131415

Client:

# -*- coding: utf-8 -*- import time import socket def main(): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: s.sendto(b'ping pang test', ('localhost', 6666)) while True: data, addr = s.recvfrom(1024) print(data,i) s.sendto(data, addr) if __name__ == '__main__': main() 123456789101112131415

30、SQL

debian系mysql安装,安装过程中会输入密码,用5.7有问题,换成5.6版本了。

sudo apt install mysql-server-5.6 1 mysql -u root -p #root账户进入数据库 create database test #创建数据库 12

修改mysql配置文件vi /etc/mysql/my.cnf,添加如下内容

[client] default-character-set = utf8 [mysqld] default-storage-engine = INNODB character-set-server = utf8 collation-server = utf8_general_ci 1234567

进入数据库输入下面内容,查看是否生效

show variables like '%char%'; 1

pip安装mysql驱动,两者选其一

pip3 install mysql-connector-python --allow-external mysql-connector-python pip3 install mysql-connector 12

下面创建表的时候要写上charset=utf8,否则不支持中文

# -*- coding: utf-8 -*- import time import socket import sqlite3 import os import mysql.connector def main(): conn = mysql.connector.connect(user='root', password='123456', database='test', charset='utf8') cursor = conn.cursor() try: cursor.execute('drop table user') except Exception: pass try: cursor.execute('create table user (id varchar(20) primary key , name varchar(20)) charset=utf8') cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'afbcd冰封飞飞']) print(cursor.rowcount) conn.commit() cursor.close() cursor = conn.cursor() cursor.execute('select * from user where id = %s' ,('1',)) values = cursor.fetchall() print(values) except Exception as e: print(e) finally: pass if __name__ == '__main__': main() 123456789101112131415161718192021222324252627282930

输出:

1 [('1', 'afbcd冰封飞飞')] 12

SQLAlchemy

使用ORM框架的话可以尝试SQLAlchemy,安装方式:

pip3 install sqlalchemy 1

31、异步IO

协程

协程是一种轻量级的线程,在一些编程语言中语法支持,python中可以通过yeild来实现协程。C和C++目前在语法层面不支持协程,需要通过一些底层的手段hack实现, 在说协程之前需要先了解几个概念。

并发(concurrency)和并行(parallelism)的区别:

并发强调的是在一个时间段,有多个任务可以执行,但是这里不要求多个任务同时执行,他们可以交替执行,类似于单核处理器,操作系统的进程调度。多个任务交替执行,在用户的眼里是同时运行的。 并行强调的是多个任务同时执行,而不是并发这种可以交替执行的行为。例如,在多核处理器上面,不同任务可以在不同的核上在同一时刻同时执行。

协程和线程的区别:

多个线程可能跑在同一个CPU核上,也可以跑在多个CPU核上,如果跑在一个CPU核上的话,多个线程就是由调度器来调度交替执行。如果绑在了不同的CPU核上的话,可以认为两个线程可以并行的执行。但是无论线程以那种形式运行,从宏观上来看,线程是“同时运行”,而且如果没有线程同步的话,多个线程之间的执行顺序是随机的,而且线程之间可能存在资源争用,访问全局资源时,通常需要加锁。 协程也是多个任务交替执行,但是协程的控制流程,函数的切换都是自己代码实现的,协程是多个任务协作完成的,但是逻辑上是连贯的,协程可以让多个任务按照你的逻辑顺序来执行,保证思维的连贯性,这种逻辑的顺序性也让协程不需要考虑全局资源争用的问题,也就不需要加锁。和线程相比,这种主动让出型调度比线程的被动调度要高效,也比操作系统的调度代价要小得多。

协程的生产者和消费者

使用多线程的生产者消费者模式,如果没有同步的话,生成者和消费者之间执行的次序会随机执行,由调度决定,而协程版本,可以由调用者来决定执行顺序,在这里我们使生产者和消费者交替执行。

def consumer(): while True: print('waiting data from producer...') data = yield print('data = {0}'.format(data)) def producer(con): data = range(1, 20) con.send(None) for i in data: print('generator data {0}'.format(i)) con.send(i) con.close() def main(): con = consumer() producer(con) 1234567891011121314151617

输出:

waiting data from producer... generator data 1 data = 1 waiting data from producer... generator data 2 data = 2 waiting data from producer... generator data 3 data = 3 waiting data from producer... generator data 4 data = 4 waiting data from producer... generator data 5 data = 5 waiting data from producer... generator data 6 data = 6 waiting data from producer... generator data 7 data = 7 waiting data from producer... generator data 8 data = 8 waiting data from producer... generator data 9 data = 9 waiting data from producer... generator data 10 data = 10 waiting data from producer... generator data 11 data = 11 waiting data from producer... generator data 12 data = 12 waiting data from producer... generator data 13 data = 13 waiting data from producer... generator data 14 data = 14 waiting data from producer... generator data 15 data = 15 waiting data from producer... generator data 16 data = 16 waiting data from producer... generator data 17 data = 17 waiting data from producer... generator data 18 data = 18 waiting data from producer... generator data 19 data = 19 waiting data from producer... 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758

yeild from

yeild from是新增的语法,PEP380讨论加入,用法:RESULT = yield from EXPRyeild from后面跟一个生成器对象,遍历生成器,将生成器yeild的每一个值返回给调用方。这样可以将嵌套的协程简化编写。 在一个生成器函数gen中执行yeild from subgen()时,subgen会获得控制权,把产出的值传给gen的调用方,即调用方可以直接控制嵌套的subgen()。与此同时,gen会阻塞,直到subgen终止执行。 下面看一个yeild from的例子

def subgen(): for i in range(5): r = yield i print('sub gen recive send{0}'.format(r)) return 'return mywait' def gen(): for i in range(2): print("hello world!") r = yield from subgen() print(r) print('hello again!') def main(): try: g = gen() next(g) while True: print(g.send('A')) except Exception as e: pass 1234567891011121314151617181920

输出:

hello world! sub gen recive sendA 1 sub gen recive sendA 2 sub gen recive sendA 3 sub gen recive sendA 4 sub gen recive sendA return mywait hello again! hello world! 0 sub gen recive sendA 1 sub gen recive sendA 2 sub gen recive sendA 3 sub gen recive sendA 4 sub gen recive sendA return mywait hello again! 12345678910111213141516171819202122232425

在这个例子中,r = yield from subgen(),执行到这句话时,函数的控制权从gen()转移到subgen()中,并且遍历执行。这里yeild出来5个值0,1,2,3,4,通过yeild from直接传递给调用方main函数。所以在print(g.send('A'))时,就打印出来了subgen() yeild的值,而在main函数send的数值,也可以直接传递给subgen()里面中去,yeild from像是一个通道打通了subgen()和main()之间的数据,gen()和subgen()之间的数据交换。r = yield from subgen()这句话的r的作用是可以接受subgen()的返回值, gen()也可以通过这样的写法来获取到subgen()给自己传递的信息。

asyncio

以上的协程只是展示了一个在单线程内,任务切换的功能。如何实现真正的异步,还需要配合消息循环和事件来处理。asyncio是python3.4新引入的标准库,内置了异步的IO。asyncio的编程模型是获取模块的主消息循环,并且把协程扔到消息循环中,实现异步IO。这样可以在协程执行耗时操作的IO操作时,交出控制权,调度器来执行其他的协程,当IO操作执行完毕时,又可以切回执行完IO操作的协程。

@asyncio.coroutine装饰器

asyncio包使用的协程是较为严格的定义,适合asyncio API的协程在定义中必须使用yield from,而不能使用yeild。@asyncio.coroutine装饰器应该使用到协程上面,并不是强求的,但是强烈建议这样做。可以将协程从普通函数中凸出出来。 下面一个例子,使用协程来实现异步操作,程序的效果是模拟执行一个耗时的操作,然后控制台绘制一个光标旋转的操作,来表示有任务正在执行。

# -*- coding: utf-8 -*- import asyncio import itertools import sys @asyncio.coroutine def spin(msg): write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-\\'): status = char + ' ' + msg write(status) flush() write('\x08' * len(status)) try: yield from asyncio.sleep(.1) except asyncio.CancelledError: break write(' ' * len(status) + '\x08' * len(status)) @asyncio.coroutine def slow_function(): yield from asyncio.sleep(3) return 42 @asyncio.coroutine def supervisor(): spinner = asyncio.ensure_future(spin('thinking')) print('spinner object:', spinner) result = yield from slow_function() spinner.cancel() return result def main(): loop = asyncio.get_event_loop() result = loop.run_until_complete(supervisor()) loop.close() print('Answer:', result) if __name__ == '__main__': main() 123456789101112131415161718192021222324252627282930313233343536373839

该程序的main()函数,将协程supervisor()加入主事件循环。 在supervisor()中,使用asyncio.ensure_future创建了一个协程任务(在python3.7+的版本,应该使用asyncio.create_task来创建。)然后使用yield from slow_function()来模拟执行一个耗时的操作。之后耗时操作执行完毕,关闭spinner协程。 slow_function()里面使用asyncio.sleep(3)来模拟一个耗时3秒的操作,对于slow_function()来说,这里会阻塞3秒的时间,而对于整个事件循环来说,这里会将控制权交给主循环,会继续执行其他的协程。这就是异步的sleep,如果使用time.sleep的话,整个主循环都会阻塞在这里,其他的协程不会被调度。 spin()函数里面循环打印字符来模拟等待。’\x08’是退格操作。 在这个例子中,slow_function()的耗时操作和spin()函数的打印是并发执行的,但是仅用到了一个线程。

async/await

python3.5使用了新的语法来表示协程,用于替代@asyncio.coroutine装饰器和yield from语法。如果使用python3.5的协程新语法的话,只需要将 @asyncio.coroutine替换为async 将yield from替换为await 使用新的语法可以更加清晰的表明协程,也和其他支持协程的语言的语法更加一致。 将上面的协程代码替换为新的语法:

# -*- coding: utf-8 -*- import asyncio import itertools import sys async def spin(msg): write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-\\'): status = char + ' ' + msg write(status) flush() write('\x08' * len(status)) try: await asyncio.sleep(.1) except asyncio.CancelledError: break write(' ' * len(status) + '\x08' * len(status)) async def slow_function(): await asyncio.sleep(3) return 42 async def supervisor(): spinner = asyncio.ensure_future(spin('thinking')) print('spinner object:', spinner) result = await slow_function() spinner.cancel() return result def main(): loop = asyncio.get_event_loop() result = loop.run_until_complete(supervisor()) loop.close() print('Answer:', result) if __name__ == '__main__': main() 1234567891011121314151617181920212223242526272829303132333435363738

aiohttp

aiohttp是一个异步的http框架,下面是使用aiohttp创建一个http server

# -*- coding: utf-8 -*- import asyncio from aiohttp import web routes = web.RouteTableDef() @routes.get('/') async def index(request): #await asyncio.sleep(0.5) return web.Response(body=b'<h1>Index</h1>', content_type='text/html') @routes.get('/hello/{name}') async def hello(request): #await asyncio.sleep(0.5) text = '<h1>hello, %s!</h1>' % request.match_info['name'] return web.Response(body=text.encode('utf-8'), content_type='text/html') def init(): app = web.Application() app.router.add_routes(routes) web.run_app(app, host='localhost', port='8080') init() 1234567891011121314151617181920

下面是一个aiohttp作为client的例子: 使用aiohttp,使用协程异步下载几个文件

# -*- coding: utf-8 -*- import time import sys import os import asyncio import aiohttp POP20_CC = ('CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR').split() BASE_URL = 'http://flupy.org/data/flags' DEST_DIR = 'downloads/' def save_flag(img, filename): path = os.path.join(DEST_DIR, filename) with open(path, 'wb') as fp: fp.write(img) def get_flag(cc): url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower()) resp = requests.get(url) return resp.content def show(text): print(text, end=' ') sys.stdout.flush() async def get_flag(cc): url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower()) async with aiohttp.ClientSession() as session: async with session.get(url) as resp: image = await resp.read() return image async def download_one(cc): image = await get_flag(cc) show(cc) save_flag(image, cc.lower() + '.gif') return image def download_many(cc_list): loop = asyncio.get_event_loop() to_do = [download_one(cc) for cc in sorted(cc_list)] wait_coro = asyncio.wait(to_do) res, _ = loop.run_until_complete(wait_coro) loop.close() return len(res) def main(download_many): t0 = time.time() count = download_many(POP20_CC) elapsed = time.time() - t0 msg = '\n{} flags downloaded in {:.2f}s' print(msg.format(count, elapsed)) if __name__ == '__main__': main(download_many) 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354

future

future指一种对象,表示异步执行的结果,是concurrent.futures模块和asyncio包重要的组件,可是这两个库的用户,有时却看不到future。我们编写的代码可能没有直接使用future。 从python3.4起,标准库中有两个名为Future的类,concurrent.futures.Future和asyncio.Future。这两个类的作用相同,都表示可能已经完成或者尚未完成的延迟计算。 future封装待完成的操作,可以放入队列,完成的状态可以查询,得到结果后可以获取结果。 通常情况下,我们不应该自己创建future,而只能由并发框架(concurrent.futures或asyncio)实例化。 下面一个例子使用concurrent.futures模块模拟下载一些内容

# -*- coding: utf-8 -*- from concurrent import futures import time import random def download_one(cc): time.sleep(random.random() * 2) print('download {}'.format(cc)) return cc def download_many(cc_list): workers = min(20, len(cc_list)) with futures.ThreadPoolExecutor(workers) as executor: res = executor.map(download_one, cc_list) return len(list(res)) def main(download_many): t0 = time.time() count = download_many(['S1', 'S2', 'S3', 'S4', 'S5', 'S6']) elapsed = time.time() - t0 msg = '\n{} flags downloaded in {:.2f}s' print(msg.format(count, elapsed)) if __name__ == '__main__': main(download_many) 1234567891011121314151617181920212223

输出:

download S1 download S4 download S3 download S5 download S2 download S6 6 flags downloaded in 1.91s 12345678

executor.map和内置map类似,不过download_one会在多个线程并发调用。和上面开头介绍的一样,这里我们并没有看到future,我们使用并发框架的时候一般不会自己去创建future。 下面的代码是我们手动创建future对象来实现

# -*- coding: utf-8 -*- from concurrent import futures import time import random def download_one(cc): time.sleep(random.random() * 2) print('download {}'.format(cc)) return cc def download_many(cc_list): cc_list = cc_list[:5] with futures.ThreadPoolExecutor(max_workers=3) as executor: to_do = [] for cc in sorted(cc_list): future = executor.submit(download_one, cc) to_do.append(future) msg = 'Scheduled for {}: {}' print(msg.format(cc, future)) results = [] for future in futures.as_completed(to_do): res = future.result() msg = '{} result: {!r}' print(msg.format(future, res)) results.append(res) return len(list(results)) def main(download_many): t0 = time.time() count = download_many(['S1', 'S2', 'S3', 'S4', 'S5', 'S6']) elapsed = time.time() - t0 msg = '\n{} flags downloaded in {:.2f}s' print(msg.format(count, elapsed)) if __name__ == '__main__': main(download_many) 1234567891011121314151617181920212223242526272829303132333435

输出:

Scheduled for S1: <Future at 0x7f1a69c009e8 state=running> Scheduled for S2: <Future at 0x7f1a69c00b38 state=running> Scheduled for S3: <Future at 0x7f1a69c00d68 state=running> Scheduled for S4: <Future at 0x7f1a69c00f28 state=pending> Scheduled for S5: <Future at 0x7f1a69c00ef0 state=pending> download S1 <Future at 0x7f1a69c009e8 state=finished returned str> result: 'S1' download S2 <Future at 0x7f1a69c00b38 state=finished returned str> result: 'S2' download S4 <Future at 0x7f1a69c00f28 state=finished returned str> result: 'S4' download S5 <Future at 0x7f1a69c00ef0 state=finished returned str> result: 'S5' download S3 <Future at 0x7f1a69c00d68 state=finished returned str> result: 'S3' 5 flags downloaded in 1.91s 1234567891011121314151617

executor.submit将任务提交对执行队列,然后返回一个future对象,这个时候对象就已经开始准备执行了。for future in futures.as_completed(to_do):这个循环是等待执行完毕,获取结果。 由于受GIL限制,上面的代码都不能并行下载,但是GIL几乎对IO密集型的处理无害,标准库中的所有执行IO操作的函数,在等待操作系统返回结果时,都会释放GIL。这意味着在Python语言这个层次上可以使用多线程,而IO密集型的程序可以从中受益。上面的代码每个任务下载需要时间0-2秒,但是并发下载完5个对象,用时并没有5倍于单次下载的时间。

ProcessPoolExecutor

如果做CPU密集的操作,可以使用ProcessPollExecutor,这个模块将工作分配给多个Python进程处理,可以绕开GIL利用所有的CPU核心。使用方式只需要把上面的ThreadPoolExecutor替换成ProcessPoolExecutor即可

async with/async for

异步中的上下文管理器with和异步迭代器使用了新语法async with和async for。具体的简介参考https://blog.csdn.net/tinyzhao/article/details/52684473

最新回复(0)