博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python学习笔记
阅读量:2172 次
发布时间:2019-05-01

本文共 50769 字,大约阅读时间需要 169 分钟。


文章目录

0、前言

本文是根据廖总结而来。

参考《python学习手册》,《流畅的python》以及python官方手册等网络资料
略过了与C和C++ 语言相同的语法部分

1、转义方式

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

例如

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

里面的内容都无需转义。

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

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

2、字符串的占位符

百分号占位符

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

例如

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

format格式化占位符

还有一种新的占位符使用{0}占位,有点像C#的格式化字符串操作。

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

输出:

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

3、布尔值

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

4、if判断

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

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

5、除法运算

C语言中,两个整数相除的结果还是整数,而在python中,结果是一个浮点数。

如果想让两个整数相除的结果变成整数的话,需要用地板除的方式,双斜线
例如:

10//3

结果是3

6、list

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

list使用中括号来表示
例如:

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

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

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

输出345

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

a.append('666')

在中间插入元素,相当于C++ 的insert,在python也是insert

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

a.pop()

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

例如

a.pop(1)

删除索引1的元素

7、tuple

tuple类似于C++ 中的tuple,和python的list也很像,只不过tuple一旦创建就不能更改里面的内容了。

tuple可以隐式转换为单个变量例如:

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

输出:

1 2 3

也可以使用封包

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

输出

1[2, 3, 4]

8、输入输出

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

print ("hello world")

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

例如

name = input("input your name")

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)

10、循环

python中有两种循环,一种是for循环一种是while循环。和C、C++ 中的不太一样。没有do while循环

for循环:类似于C++中的for (auto i : vec)这种range-based的for循环

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

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

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

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

11、字典dict

字典类似与C++ 中的unordered_map,是一种哈希表的key-value的查找结构。

使用大括号来表示:

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

判断一个key在不在字典里面可以使用

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

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

结果:

3

遍历dict:

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

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)

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

s1 & s2, s1 | s2

13、函数

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

a = absa(-10)

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

abs = 10abs(-10)

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

python包含了很多内置的函数,可以从查看

函数的定义使用def

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

空语句可以使用pass,类似于C/C++ 中的分号

可变参数:在参数前面加一个星号,就变成了可变参数,可以传入任意个数的参数,实际上是自动封装成了一个tuple。

可变参数

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

调用时可以myFunc(1, 2, 3)这样调用,

也可以传入一个list或者tuple

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

关键字参数

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

这个函数里面第三个参数是**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)

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

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

命名关键字参数:

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

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

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

这个星号必不可少,如果正好有一个可变参数,那就不需要额外的星号了

def func(a, b, *args, name, job):也是可以的

参数组合

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

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

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

函数注释/注解

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

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

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

可以使用

print(func.__annotations__)

打印函数注解,结果如下

{
'a': 'spam', 'b': (1, 10), 'c':
, 'return':
}

匿名函数lambda

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

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

装饰器

装饰器是一种委托的调用方式,将被装饰的函数包装一层,增加一些定制处理。

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

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

结果:

test func:5

偏函数

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

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

结果:

21

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[:])

结果:

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]

切片是一个左闭右开的区间,例如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))

输出结果:

ab12a : 1b : 2

对于字典,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))

输出

TrueFalse

下标循环

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

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

输出

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

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

例如改为:

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

结果就是

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

应用迭代的函数

sorted

对参数进行排序

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

结果

[1, 2, 3, 4]

zip

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

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

结果

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

enumerate

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

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

结果

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

filter

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

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

结果

[1, 2]

map

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

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

结果

[3, 5, 7.0, 9.1]

reduce

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

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

结果

5050

sum

对参数求和

any

对参数全部进行or运算

all

对参数全部进行and运算

max

求参数最大值

min

求参数最小值

16、列表生成式

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

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

结果为:

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

列表生成式

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

结果为:

[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]

可以用一句话来使用嵌套循环和条件判断语句以及一个表达式来生成一个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 = 1for 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

结果:

1x1 = 1 1x2 = 2 1x3 = 3 1x4 = 4 1x5 = 5 1x6 = 6 1x7 = 7 1x8 = 8 1x9 = 92x2 = 4 2x3 = 6 2x4 = 8 2x5 = 10 2x6 = 12 2x7 = 14 2x8 = 16 2x9 = 183x3 = 9 3x4 = 12 3x5 = 15 3x6 = 18 3x7 = 21 3x8 = 24 3x9 = 274x4 = 16 4x5 = 20 4x6 = 24 4x7 = 28 4x8 = 32 4x9 = 365x5 = 25 5x6 = 30 5x7 = 35 5x8 = 40 5x9 = 456x6 = 36 6x7 = 42 6x8 = 48 6x9 = 547x7 = 49 7x8 = 56 7x9 = 638x8 = 64 8x9 = 729x9 = 81

集合生成式

和字典生成式没啥区别,只是把表达式换成一个值就可以了

有列表,字典,集合生成式但是没有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))

输出:

14916

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

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

输出:

149162536496481

使用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))

结果:

011235

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))

结果:

0x=51x=None4x=None9

生成器的return

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

18、迭代器

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

from collections.abc import Iterableisinstance(obj, Iterable)

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

from collections.abc import Iteratorisinstance(obj, Iterator)

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

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

单个迭代器和多个迭代器

有些对象支持单个迭代器,有些支持多个迭代器。单个迭代器是指,使用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))

结果:

001

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))

结果:

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

19、闭包

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

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

20、模块

模块通常是一个文件,在python中,模块也是一个对象。模块内的变量,函数,类都是模块的属性。

python文件开头很常见的两个注释

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

main函数

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

if __name__=='__main__':    test()

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 resultdef main():    a = B()    print(a)    if __name__ == '__main__':    main()

结果:

age=50name=Acolor=Red_C__private=C

__slots__

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

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

结果:

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

实际上这种做法是把类的__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 docclass 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()

结果:

FalseHelp 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 ageNone

重载运算符

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@uniqueclass WeekDay(Enum):    Sun = 0 # Sun的value被设定为0    Mon = 1    Tue = 2    Wed = 3    Thu = 4    Fri = 5    Sat = 6def main():   print(WeekDay['Mon'])   print(WeekDay.Sat)   print(WeekDay(2))   print('------------')   for k,v in WeekDay.__members__.items():       print(k,v,v.value)

结果:

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

元类

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

type

type可以查看一个变量的类型,type也可以动态的创建一个类。

参数1:类名
参数2:基类
参数3:方法dict

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

结果:

hello world

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):    passdef main():   a = myClass()   a.test([1,2])

结果:

[1, 2]

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()

结果:

my Exceptionfinally

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

断言assert

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

assert False, 'hello'

log

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

import logginglogging.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()

结果:

INFO:root:info levelWARNING:root:warning levelERROR:root:error level

pdb

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

python3 -m pdb helloworld.py

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

import pdbpdb.set_trace()

详细的pdb使用参考

单元测试

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

下面是一个单元测试的例子

import unittestclass 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 ValueErrorclass 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()

结果:

set upstart test_0_to_60end test_0_to_60tear down.set upstart test_60_to_80end test_60_to_80tear down.set upstart test_80_to_100end test_80_to_100tear down.set upstart test_invalidend test_invalidtear down.----------------------------------------------------------------------Ran 4 tests in 0.000sOK

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

python3 -m unittest helloworld

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

python3 helloworld.py

但是此方法在我的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='')

输出:

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

gbkhello world

使用正确的编码测试:

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='')

输出:

测试gbkhello world哈哈哈

StringIO, BytesIO

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

from io import StringIOfrom io import BytesIOdef 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)

输出:

testhello world哈哈哈-------------------newtest-------------------b'\xe6\xb5\x8b\xe8\xaf\x95'

文件和目录

python的os模块包含了一些操作系统相关的操作。

获取环境变量

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

获取当前目录

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

连接目录,因为不同操作系统的目录分隔符不一样,所以需要使用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__']>>>

分割目录

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

分割扩展名

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

重命名

>>> 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__']

删除文件

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

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

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

序列化

pickle

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

import pickledef 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()

输出:

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

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

import pickledef 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()

输出:

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]}

json

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

import jsondef 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()

输出:

{"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}

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 = aliveimport jsondef 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__)

输出:

{"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}

24、进程和线程

进程

详细内容参考

fork

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

Process

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

Pool

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

子进程

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

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

结果:

$ ping www.python.orgPING 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 ms64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=2 ttl=51 time=238 ms64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=3 ttl=51 time=231 ms64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=4 ttl=51 time=229 ms64 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 4003msrtt min/avg/max/mdev = 225.711/231.863/238.158/4.302 msExit code: 0

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

import subprocessdef 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()

输出:

$ import thisThe Zen of Python, by Tim PetersBeautiful 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

进程间通讯

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

from multiprocessing import Process, Queuedef 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()

输出:

process producer pid(16750)put value Aprocess consumer pid(16751)start valueget value Astart valueput value Bget value Bstart valueput value Cget value Cstart valueput value Dget value Dstart valueput value Eget value Estart value

线程

python中的线程使用threading模块

import threadingimport timedef 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()

输出:

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.

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()

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

线程局部变量

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

import threadingimport timethread_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()

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', 'A1', '2', '3fd', 's', 'a', 'f', 'daA123A321Af']

分组

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())

输出:

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

编译正则表达式

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())

输出:

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

26、常用内建模块

datatime

from datetime import datetimedef 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()

输出:

2018-10-07 21:41:19.8976502017-03-16 00:00:001538919679.8976872015-04-19 12:20:002018-10-05 18:55:22Sun, Oct 07 21:41

collections

from collections import namedtuplefrom collections import dequefrom collections import defaultdictfrom collections import OrderedDictfrom collections import Counterdef 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()

输出:

p = Point(x=1, y=2)deq = deque(['z', 'a', 'b', 'c', 'd'])deq = deque(['a', 'b', 'c'])N/Aod = 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})

base64

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

import base64def 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()

输出:

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

struct

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

字符 字节序 尺寸 对齐
< 小端序 标准
> 大端序 标准
! 网络序 标准
@ 主机序 native native
= 主机序 标准
格式 C语言类型 Python 类型 标准尺寸
x 填充类型 no value
c char 长度为1的bytes 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer 4
l long integer 4
L unsigned long integer 4
q long long integer 8
Q unsigned long long integer 8
n ssize_t integer
N size_t integer
e float 2
f float float 4
d double float 8
s char[] bytes
p char[] bytes
P void* integer
import structdef main():   a =  struct.pack('=hHQ',-100, 100,100000000000)      print(a.hex())    if __name__ == '__main__':    main()

输出:

9cff640000e8764817000000

hashlib

import hashlibdef 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())

输出:

725b5fca27c243ab4e61b02b0b74b6e2725b5fca27c243ab4e61b02b0b74b6e2bc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdcbc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdc

hmac

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

输出:

4599b96ad2c95e565f4f8d29c925a3eb0d3549b0b913d996b18393a02a03ea42

itertools

import itertoolsdef 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 连接多个迭代对象

groupby

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

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

输出:

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

contextlib

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

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

我们可以通过重载__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

输出:

enterEnd

也可以使用@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()

输出:

beginend

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

@contextmanagerdef tag(name):    print("<%s>" % name)    yield    print("
" % name)with tag("h1"): print("hello") print("world")

输出:

helloworld

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

@contextmanagerdef closing(thing):    try:        yield thing    finally:        thing.close()

urllib

用于操作URL

XML

from xml.parsers.expat import ParserCreate

HTMLParser

from html.parser import HTMLParser

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 socketimport chardetimport threadingdef 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()

Client代码:

# -*- coding: utf-8 -*-import timeimport socketdef 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()

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

bingfengfeifei@bingfengfeifei-PC:nc localhost 6666

UDP

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

Server:

# -*- coding: utf-8 -*-import socketimport chardetimport threadingdef 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()

Client:

# -*- coding: utf-8 -*-import timeimport socketdef 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()

30、SQL

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

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

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

[client]default-character-set = utf8[mysqld]default-storage-engine = INNODBcharacter-set-server = utf8collation-server = utf8_general_ci

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

show variables like '%char%';

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

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

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

# -*- coding: utf-8 -*-import timeimport socketimport sqlite3import osimport mysql.connectordef 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:        passif __name__ == '__main__':    main()

输出:

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

SQLAlchemy

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

pip3 install sqlalchemy

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)

输出:

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

yeild from

yeild from是新增的语法,,用法: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

输出:

hello world!sub gen recive sendA1sub gen recive sendA2sub gen recive sendA3sub gen recive sendA4sub gen recive sendAreturn mywaithello again!hello world!0sub gen recive sendA1sub gen recive sendA2sub gen recive sendA3sub gen recive sendA4sub gen recive sendAreturn mywaithello again!

在这个例子中,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 asyncioimport itertoolsimport sys@asyncio.coroutinedef 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.coroutinedef slow_function():    yield from asyncio.sleep(3)    return 42@asyncio.coroutinedef supervisor():    spinner = asyncio.ensure_future(spin('thinking'))    print('spinner object:', spinner)    result = yield from slow_function()    spinner.cancel()    return resultdef main():    loop = asyncio.get_event_loop()    result = loop.run_until_complete(supervisor())    loop.close()    print('Answer:', result)if __name__ == '__main__':    main()

该程序的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 asyncioimport itertoolsimport sysasync 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 42async def supervisor():    spinner = asyncio.ensure_future(spin('thinking'))    print('spinner object:', spinner)    result = await slow_function()    spinner.cancel()    return resultdef main():    loop = asyncio.get_event_loop()    result = loop.run_until_complete(supervisor())    loop.close()    print('Answer:', result)if __name__ == '__main__':    main()

aiohttp

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

# -*- coding: utf-8 -*-import asynciofrom aiohttp import webroutes = web.RouteTableDef()@routes.get('/')async def index(request):    #await asyncio.sleep(0.5)    return web.Response(body=b'

Index

', content_type='text/html')@routes.get('/hello/{name}')async def hello(request): #await asyncio.sleep(0.5) text = '

hello, %s!

' % 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()

下面是一个aiohttp作为client的例子:

使用aiohttp,使用协程异步下载几个文件

# -*- coding: utf-8 -*-import timeimport sysimport osimport asyncioimport aiohttpPOP20_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.contentdef 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 imageasync def download_one(cc):    image = await get_flag(cc)    show(cc)    save_flag(image, cc.lower() + '.gif')    return imagedef 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)

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 futuresimport timeimport random def download_one(cc):    time.sleep(random.random() * 2)    print('download {}'.format(cc))    return ccdef 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)

输出:

download S1download S4download S3download S5download S2download S66 flags downloaded in 1.91s

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

下面的代码是我们手动创建future对象来实现

# -*- coding: utf-8 -*-from concurrent import futuresimport timeimport random def download_one(cc):    time.sleep(random.random() * 2)    print('download {}'.format(cc))    return ccdef 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)

输出:

Scheduled for S1: 
Scheduled for S2:
Scheduled for S3:
Scheduled for S4:
Scheduled for S5:
download S1
result: 'S1'download S2
result: 'S2'download S4
result: 'S4'download S5
result: 'S5'download S3
result: 'S3'5 flags downloaded in 1.91s

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。具体的简介参考

转载地址:http://ojezb.baihongyu.com/

你可能感兴趣的文章
深入理解JVM虚拟机4:Java class介绍与解析实践
查看>>
深入理解JVM虚拟机5:虚拟机字节码执行引擎
查看>>
深入理解JVM虚拟机6:深入理解JVM类加载机制
查看>>
深入了解JVM虚拟机8:Java的编译期优化与运行期优化
查看>>
深入理解JVM虚拟机9:JVM监控工具与诊断实践
查看>>
深入理解JVM虚拟机10:JVM常用参数以及调优实践
查看>>
深入理解JVM虚拟机11:Java内存异常原理与实践
查看>>
深入理解JVM虚拟机12:JVM性能管理神器VisualVM介绍与实战
查看>>
深入理解JVM虚拟机13:再谈四种引用及GC实践
查看>>
Spring源码剖析1:Spring概述
查看>>
Spring源码剖析2:初探Spring IOC核心流程
查看>>
Spring源码剖析3:Spring IOC容器的加载过程
查看>>
Spring源码剖析4:懒加载的单例Bean获取过程分析
查看>>
Spring源码剖析5:JDK和cglib动态代理原理详解
查看>>
Spring源码剖析6:Spring AOP概述
查看>>
Spring源码剖析7:AOP实现原理详解
查看>>
Spring源码剖析8:Spring事务概述
查看>>
Spring源码剖析9:Spring事务源码剖析
查看>>
重新学习Mysql数据库1:无废话MySQL入门
查看>>
探索Redis设计与实现2:Redis内部数据结构详解——dict
查看>>