流畅的python在线阅读-流畅的python笔记

鸭子类型协议不完全总结

序列:len,getitem

切片:getitem

v[0]分量的取值和写值:getitem和setitem

v.x属性的取值和写值:getattr和setattr

迭代:1)iter,2)getitem

上下文管理器:enter,exit

+=:1)iadd,2)add

可散列:hash,eq

in测试:1)contains,2)iter,3)getitem

第一章,python数据模型

__repr__得到对象的字符串表现形式

if obj,实际执行的是bool(obj),python解释器调用obj.__bool__,如果没有__bool__,会调用__len__看是否为0

len(obj),python解释器调用obj.__len__()

obj[key],python解释器调用obj.__getitem__(key)

"hello world %s" % x, "hello world %r" % x ,对于x=123两者输出都一样,x="abc"前者输出hello world abc,后者输出hellow world "abc"

第二章,序列构成的数组

容器序列:引用,list、tuple、collections.deque

扁平序列:值,str、bytes、array.array

可变序列:list、array.array

不可变序列:tuple、str

*args :序列中的值

args:序列

列表推导不再会有变量泄漏的问题

元组拆包:_,a, b, *args = [1, 2, 3, 4, 5, 6],不要的可以用_或者*args

切片实际操作步骤:首先生成切片对象slice(a, b, c),再使用__getitem__方法取值

序列使用 + 和 * 时,原序列都不会被修改,而是新建序列

创建3个空列表:应该使用[[] for i in range(3)],而不是使用[[]] * 3

array.array:浮点数效率高

set:in测试效率高

第三章,字典和集合

用户自定义类型的对象都是可散列的,散列值就是id()返回的值

dict.get(k, w):查找key时,设置默认

dict.setdefault(k, w):更新key时,设置默认

defaultdict(type):可以设置字典value的缺省类型,例如list

__missing__:找不到key时会调用__missing__特殊方法

集合没有getitem,但是有iter

第四章,文本和字节序列

待补充

第五章,一等函数

函数本质上就是定义了__call__的类,此外还有些函数特有的属性,例如__kwdefaults__,__defaults__,__get__等等

建议用生成器表达式取代map,filter

匿名函数lambda,主要适用于传参,可以省略函数名

operator:提供了一些算数运算符函数,例如相乘mul

functools.partial(func, arg):用于冻结参数,func是可调用对象,arg是冻结的参数

第六章,使用一等函数实现设计模式

用类实现策略模式:用类实现具体策略,相同部分作为基类,不同部分作为子类

用函数实现策略模式:用函数实现具体的策略,函数作为参数传递给类的实例

第七章,函数装饰器和闭包

自由变量:未在本地作用域中绑定的变量,可以被内部嵌套函数访问,并能保留下来的

nonlocal:声明变量为自由变量

基本装饰器(2层嵌套):必须return2次,如果只return wrapper,则被装饰函数有返回值时会永远返回None

参数化装饰器(3层嵌套):支持装饰器传参

基本装饰器代码实现:

def clock(fn):

@functools.wraps()

def wrapper(*arg, **kwargs):

ret = fn(*arg, **kwargs)

return ret

return wrapper

第八章,对象引用、可变性和垃圾回收

变量是标注,不是盒子:引用式变量(例如列表、字典)可以同时有多个不同的变量名

引用性变量的不同变量名,值/id都是相等的

python的"==":只判断值是否相等,id可以不等。有点类似js的"==="

可以用is判断id是否相同,不必使用id()函数

元组的不可变性:保存的引用不可变,但是引用的对象可以变

创建别名:b = lista

浅复制:b = lista[:],b = lista.copy(),b= copy.copy(lista)

深复制:b= copy.deepcopy(lista)

如果lista列表内部有“可变的引用对象”(例如字典、集合、列表,元组算不可变的引用对象),发生改变时,会影响到浅拷贝,但不会影响到深拷贝。

防御可变参数:函数传参时,可变参数的默认值必须使用None。如果使用可变参数作为默认值,则不传参时,多个函数调用方会共享同一个可变参数。

python是按引用传参的,对参数修改后:1)不可变参数会重新开辟内存,实质相当于传统的按值传参;2)可变参数会就地修改,实质相当于传统的按引用传参。

第九章,符合python风格的对象

尽早捕获错误:如果涉及到参数转换,在__init__中完成可以尽早捕获错误

classmethod:静态函数,第一个参数是类,可以调用类属性/类方法

staticmethod:静态函数,没有特殊参数,本质上就是放在类中的全局函数

格式化:format

私有属性:__x可以通过“object_classname__x”来访问,所以本质上单下划线和双下划线没什么区别

属性设置为只读:私有属性 + @property

__slots__属性:节省空间,使用后无法新增属性

第十章,序列的修改、散片和切片

获取类的引用方法:type(self),self.__class__,被@classmethod装饰的函数的第一个参数。要访问类变量,必须先获取类的引用。

实现了一个多维向量类(序列):

v1:兼容2d,一些基本的序列方法

v2:实现getitem,支持分量访问、切片访问。没有实现setitem,所以不支持分量设值、切片设值。

v3:实现getattr和setattr,支持属性访问,setattr必须同时实现,否则对属性进行设值后(v.x)会与分量值(v[0])产生冲突

v4:实现eq和hash,可散列

v5:格式化

第十一章,从协议到抽象基类

鸭子类型:协议是非正式的接口

白鹅类型:使用抽象基类正式明确接口

abc:自定义抽象基类,抽象方法

collections.abc:提供一些内置的抽象基类

numbers:抽象基类数字塔。包括Number,Complex,Real,Rational,Intergral。

抽象基类(abc.ABC):抽象基类必须继承abc.ABC,才能约束子类必须实现抽象方法,抽象基类不能被实例化

抽象方法(@abc.abstractmethod):抽象基类的抽象方法,不用实现,子类继承抽象基类时必须实现抽象方法,才能被实例化

抽象基类主要作用:作为超类;isinstance检查;注册(xx.register);自动识别(不用注册或继承也能通过isinstance检查)

isinstance检查举例:collections.abc.Sequence抽象基类包含的方法有__contains__,__iter__,__len__,__getitem__,__reversed__,index,count,其中抽象方法是__len__,__getitem__。如果某对象想通过isinstance(obj, collections.abc.Sequence)检查,方法一是继承Sequence并实现2个抽象方法,方法二是自己实现所有的方法,并注册Sequence.register。

猴子补丁:直接给类(注意不是类的实例)添加新的方法。这样可以在运行时修改类或模块,而不改动源码。

python是动态强类型语言:动态指运行时检查类型,强类型指很少隐式转换类型。

第十二章,继承的优缺点

内置类型字典更新值的方法:

DoppeDict(one=1):调用__init__()

DoppeDict["two"] = 2:设置时调用__setitem__(),获取时调用__getitem__()

DoppeDict.update("three", 3):调用update(),

子类化内置类型时,不会调用用户定义的类覆盖的特殊方法

多重继承:广度优先,深度优先,python3是广度优先

委托给父类执行:super().func(),多重继承时可以指定由哪个父类执行xx.func()

继承UserDict, UserList, UseString:速度慢,以扩展

继承Dict, List, String:速度快,难扩展

第十三章,正确重载运算符

一元: -neg,+pos,~invert

算术: +add,

比较: ==eq,!=ne

增量赋值: +=iadd

运算时,先正向,后反向,比较运算还会比较id,通过返回NotImplemented切换

python3.5新引入@做点积运算

第十四章,可迭代的对象、迭代器和生成器

iterable具有可调用的iterator,可以通过iter(iterable)从iterable中获得iterator。

iterator的实现方法:1)iter + next,其中iter方法中return self,next方法中逐个返回值。2)iter,iter方法中使用yield生成器返回值。

generator属于iterator。

Sequence实现了getitem和len特殊方法就可以被迭代,因为解释器在尝试迭代对象x时,会执行如下步骤:1)调用iter(x);2)没有iter就调用getitem;

StopIteration表示迭代器到头。

典型的迭代器:2个类,iterable和iterator作为2个类分开实现,iterable中的iter返回iterator;iterator中的iter返回self,next逐个返回值。

糟糕的迭代器:1个类,同时在iterable中实现了iterator,即iterable中的iter返回self,同时具有next方法逐个返回值。

生成器:1个类,iterable的iter返回yield生成器。

惰性生成器:1个类,iterable的iter返回yield生成器,但是生成器的内容是惰性获取,例如获取文本时由finditer替代findall。

生成器表达式:1个类,iterable的iter返回一个生成器表达式。

多层for循环时,可以用yield from替代内层for循环

第十五章,上下文管理器和else块

用法:with xxx as yyy

支持同时打开两个对象:with xxx as yyy, aaa as bbb

上下文管理器协议:__enter__方法中返回yyy(没有提供内容时直接返回None),__exit__方法中做好退出释放资源操作

第十六章,协程

进程:有独立内存空间。

线程:CPU调度最小单位,共享内存。

协程:用户态的轻量级线程,上下文切换块,CPU感知不到协程,只使用一个线程。

在其他语言中,协程意义不大,可以通过多线程解决I/O,但是python有GIL多线程也是伪多线程,同一时间只能有一个线程,同步编程I/O阻塞称为瓶颈,可以用协程处理高I/O操作。

同步编程并发:单纯通过多进程和多进程进行同步编程,切换线程和切换进程有开销,还需要解决进程间/线程间资源交互问题。

异步编程并发:协程,回调。

协程: a = yield b,先执行b产出值;再执行a赋值;可以没有a,即只产出不赋值(生成器);可以没有b,即只赋值不产出值(最后一起返回)。

协程流程:

1)预激:手动next; 装饰器; yield from;

2)发送至:send;

3)关闭:发送哨符值; close(); throw()输入未捕捉的异常

4)异常处理:throw()输入except捕捉的异常

5) 获取协程返回值:PEP380定义; yield from;

第十七章,使用future处理并发

I/O密集型:多线程threading —> futures.ThreadPoolExecutor

CPU密集型:多进程multiprocessing —> futures.ProcessExecutor

python有GIL,同一时间只能允许一个线程执行python字节码,而线程是调度CPU的最小单位,因此多线程无法满足CPU密集型操作。其他语言开启多线程可以调度多个CPU的可以满足CPU密集型操作。

futures多进程/多线程处理方法:

map:全部完成后获取结果,再执行后续代码,只能处理相同的函数(函数参数可以不同)

submit + as_completed:有完成的就先获取结果,可处理不同函数

第十八章,使用asyncio包处理并发

asyncio用于异步IO

python3.5开始引入了新语法async和await:@asyncio.coroutine替换为async def …,yield from替换为await

第十九章,动态属性和特性

如果想把属性x变成只读:可以通过@property完成,特性名称需要与init中静态属性名称不同,因为如果相同没有提供setter方法无法完成赋值。

如果想限制静态属性:可以增加@x.setter装饰同名函数,静态属性名称self.x,@property特性名称x,@x.setter装饰的名称x三者相同,这时setter中的函数可以实现一些限制和验证操作,如果不做这些操作那就可以不用property和setter。

第二十章,属性描述符

限制静态属性的方法:

1)init函数中判断并raise error,缺点是只有实例化时有效,实例化后无法限制对属性进行更改

2)@property + @xx.setter,缺点是不能重复使用,有多个属性想限制时代码量会非常大

3)描述符

第二十一章,元类编程

待补充

你可能感兴趣的:(流畅的python在线阅读-流畅的python笔记)