Alan Hou的个人博客

Python高级编程、asyncio和高并发爬虫

一、Python中一切皆对象

函数和类也是对象,属于python的一等公民

1. 赋值给一个变量

2. 可以添加到集合对象中

3. 可以作为参数传递给函数

4. 可以当做函数的返回值

type、object和class的关系

object 是最顶层基类,type是一个类,同时也是一个对象

Python中的内置类型

对象的三个特征:身份(id())、类型(type())、值

类型:None(全局只有一个)、数值(int, float, complex, bool)、迭代类型、序列类型(list, bytes, bytearray, memoryview, range, tuple, str, array)、映射(dict)、集合(set, frozenset)、上下文管理类型(with)、其它类型

二、魔法函数

__init__等以双下划线开头、双下划线结尾的系统内置函数

非数学运算

字符串表示
__repr__、__str__
集合、序列相关
__len__、__getitem__、__setitem__、__delitem__、__contains__
迭代相关
__iter__、__next__
可调用
__call__
with上下文管理器
__enter__、__exit__
数值转换
__abs__、__bool__、__int__、__float__、__hash__、__index__
元类相关
__new__、__init__
属性相关
__getattr__、 __setattr__、__getattribute__、setattribute__、__dir__
属性描述符
__get__、__set__、 __delete__
协程
__await__、__aiter__、__anext__、__aenter__、__aexit__

数学运算

一元运算符
__neg__(-)、__pos__(+)、__abs__
二元运算符
__lt__(<)、 __le__ <= 、 __eq__ == 、 __ne__ != 、 __gt__ > 、 __ge__ >=
算术运算符
__add__ + 、 __sub__ – 、 __mul__ * 、 __truediv__ / 、 __floordiv__ // 、 __
mod__ % 、 __divmod__ divmod() 、 __pow__ ** 或 pow() 、 __round__ round()
反向算术运算符
__radd__ 、 __rsub__ 、 __rmul__ 、 __rtruediv__ 、 __rfloordiv__ 、 __rmod__ 、
__rdivmod__ 、 __rpow__
增量赋值算术运算符
__iadd__ 、 __isub__ 、 __imul__ 、 __itruediv__ 、 __ifloordiv__ 、 __imod__ 、
__ipow__
位运算符
__invert__ ~ 、 __lshift__ << 、 __rshift__ >> 、 __and__ & 、 __or__ | 、 __
xor__ ^
反向位运算符
__rlshift__ 、 __rrshift__ 、 __rand__ 、 __rxor__ 、 __ror__
增量赋值位运算符
__ilshift__ 、 __irshift__ 、 __iand__ 、 __ixor__ 、 __ior__

三、深入类和对象

鸭子类型:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

这种类型不是 tuple, list,而是iterable, callable这些

isinstance 和 type 的区别(推荐使用isinstance 来判断对象)

MRO(Method Resolution Order)

Python2.2以前的版本:经典类(classic class)  DFS(深度优先搜索)

Python2.2版本起有了新式类(继承 object),使用 BFS(广度优先搜索),经典类仍使用 DFS

Python 3: C3算法

print(Obj.__mro__) 可打印出顺序

实例方法、静态方法(@staticmethod)、类方法(@classmethod)

私有属性:__name,但并不绝对安全的,可以通过 obj._Classname__attr 的方式调用

Python 的自省机制:自省是通过一定的机制查询到对象的内部结构(obj.__dict__, ClassName.__dict__, dir(obj), dir(ClassName))

super 函数(子类中执行super().__init__()),注意 super 函数不一定调用的是父类,而是依据前述 MRO的顺序来执行

Mixin 模式特点
1、 Mixin功能单一
2、不和基类关联,可以和任意基类组合
3、在 Mixin 中不要使用 super

上下文管理器协议(with 语句)

__enter__, __exit__

简化方式 contextlib

四、自定义序列类

按从不同维度可分为容器序列、扁平序列和可变序列、不可变序列

容器序列(可放置不同数据类型):list、tuple、deque

扁平序列:str、bytes、bytearray、array.array

可变序列:list、 deque、bytearray、array

不可变序列:str、tuple、bytes

+, +=和 extend 的区别:

+只能接受相同的序列类型

+=右侧可以接受任意序列类型,通过__iadd__魔法函数实现,该方法内部使用 extend 来实现

append 会将传入的序列作为一个值而非遍历地加入原序列

切片

知识回顾

通过实现__getitem__等魔法函数即可为自定义序列添加切片等功能,示例如下:

使用 bisect 来维护排序序列效率是非常高的

列表使用list 非常普遍,但有时 array(只能存放指定类型), deque 会更好

array.array(“i”)   array 的类型参见:https://docs.python.org/3/library/array.html

列表推导式、生成器表达式、字典推导式

五、深入python的set和dict

dict属于 MutableMapping 类型,MutableMapping继承 Mapping,Mapping 继承 Collection

浅拷贝 dict 内部的 dict 或 list 元素在拷贝后只是一个指向,因而在新的 dict 内进行修改也会修改原 dict,而深拷贝则是一份彻底的拷贝。此外,不建议继承 List 和 Dict,因其均使用 C语言书写,正常的继承不会生效,如确需操作,可能过继承 collections.UserDict等方式。

set(集合)、frozenset(不可变集合):无序、不重复,frozenset 可以作为 dict 的 key

set的性能很高,set 添加数据可以通过 add 方法

dict查找的性能远远大于list,list 的查找时间随着数据增大而增大,而 dict 的查找时间则几乎没有变化

1. dict的key或者set的值 都必须是可以hash的
不可变对象 都是可hash的, str, fronzenset, tuple,自己实现的类 __hash__
2. dict的内存花销大,但是查询速度快, 自定义的对象 或者python内部的对象都是用dict包装的
3. dict的存储顺序和元素添加顺序有关
4. 添加数据有可能改变已有数据的顺序

六、对象引用、可变性和垃圾回收

Python 和 Java 中的变量本质不一样,Python 的变量实质上是一个指针

is 判断两个变量的 id/内存地址 是否相同,==判断两个变量值是否相等,但需要注意的是小整数和短字符串由于 Python 的 intern 机制 id 值也相同

Python 中垃圾回收的算法采用引用计数,只有在计数器等于0时才会回收对象

经典错误

七、元类编程

通过@property可以以属性的方式来调用方法,如果要用属性的方法来设定值,则通过@xxx.setter 装饰器来定义方法

__getattr__在查找不到属性时调用, __getattribute__访问任何属性都会调用

属性描述符

实现__get__, __set__, __delete__任意方法称为属性描述符,实现了__get__和__set__的称为数据属性描述符,仅实现了__get__方法的称为非数据属性描述符

如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))首先调用__getattribute__,如果类定义了__getattr__方法,那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
user = User(), 那么user.age 顺序如下:

(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__方法, 否则

(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则

(3)如果“age”出现在User或其基类的__dict__中

(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则

(3.2)返回 __dict__[‘age’]

(4)如果User有__getattr__方法,调用__getattr__方法,否则

(5)抛出AttributeError

__new__和__init__的区别

__new__用来控制对象的生成过程,在对象生成之前执行,__init__用来完善对象;如果__new__方法不返回对象(return super().__new__(cls)),则不会调用__init__方法

元类 Meta Class

八、迭代器和生成器

迭化器是访问集合内元素的一种方式,一般用来遍历数据。
迭代器和以下标的访问方式不一样,迭代器是不能返回的,迭代器提供了一种惰性方式的数据访问。列表等可迭代数据类型通过 __iter__来实现迭代。

获取字节码对象:dis.dis(func)

Python 生成器原理

生成器对象结构

九、Python Socket编程

应用层和传输层之前可以有一个 Socket 接口

五层网络模型

扩展阅读:TCP/IP 详解卷1-卷3

Socket 编程


Socket 编程[/caption]

十、多线程、多进程和线程池编程

GIL: Global Interpreter Lock
Python 中一个线程对应于 C 语言中的一个线程, GIL使得同一时刻只有一个线程在一个 CPU 上执行字节码,无法将多个线程映射到多个 CPU 上执行。GIL 会根据执行的字节码行数以及时间片释放 GIL,另外GIL 在遇到 io 操作的时候也会主动释放。

多进程编程

耗 CPU 的操作(数学计算、图像处理),用多进程操作,对于 IO 操作使用多线程编程,因为就操作系统而言进程切换代价要高于线程。

共享全局变量不适用于多进程编程,进程间数据是隔离的,但可以通过实例化 Manager().dict()在实现该数据共享。

multiprocessing 中的 Queue 不能用于 pool 进程池,pool 中进程间通信需要使用 Manager 中的 Queue

通过 Pipe 实现进程间通信,Pipe 性能高于 Queue,但Pipe 只能适用于两个进程

十一、协程和异步io

并发是指一个时间段内,有几个程序在同一个 CPU 上运行,但任意时刻只有一个程序在 CPU 上运行

并行是批在任意时刻点上,有多个程序同时运行在多个CPU上

同步是指代码调用 IO 操作时,必须等待 IO 操作完成才返回的调用方式

异步是指代码调用 IO 操作时,不必等待 IO 操作完成就返回的调用方式

阻塞是指调用函数时当前线程被挂起

非阻塞是指调用函数时当前线程不会被挂起,而是立即返回

Unix 下的五种I/O模型:阻塞式 I/O、非阻塞式 I/O、I/O复用、信号驱动式I/O、异步I/O

select, poll, epoll

生成器

生成器不止可以产出值,还可以接收值

在调用 send 发送非 None 值之前,必须启动一次生成器,方式有两种:

1、gen.send(None),2、next(gen)

send 方法可以传递值到生成器内部,还可以重启生成器到下一个位置

GeneratorExit继承自 BaseException,而非 Exception

十二、asyncio并发编程

asyncio

aiohttp实现高并发爬虫

相关资源:

如何安装Python3.7

退出移动版