文章目录
多重继承
- 基本概念:多重继承-廖雪峰
顺序问题&同名方法
问题:如果不同的父类中存在 同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
-
Python 中的 MRO (方法搜索顺序)
- Python 中针对 类 提供了一个内置属性 mro 可以查看方法搜索顺序
- MRO 是 method resolution order,主要用于在多继承时判断 方法、属性 的调用 路径
print(C.__mro__) #C是多继承后的类名
- 参考:Python多继承与super使用详解
-
子类从多个父类派生,而子类又没有自己的构造函数时:
- 按顺序继承,哪个父类在最前面且它又有自己的构造函数,就继承它的构造函数;
- 如果最前面第一个父类没有构造函数,则继承第2个的构造函数,第2个没有的话,再往后找,以此类推。
-
super()与调用父类方法:
- super().__init__相对于类名.init,在单继承上用法基本无差;但在多继承上,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次。
- 对于多重继承,如果有多个构造函数需要调用, 我们必须用传统的方法:
父类.__init__(self)
-
同名方法:ABCD是指编写的先后顺序。
类的魔法方法(class.__fun__()
)
这归功于动态语言的“鸭子类型”,不需要强制继承某个接口。
- 初始化/实例化:
__init__
- len():
__len__
- with:
__enter__
和__exit__
- 限制示例属性:
__slots__
:定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。 - str() / print():
__str__
- 调试时:
__repr__
- 返回一个迭代对象,
for...in
:__iter__
next()
拿到生成器下一个值:__next__
- 列表按下标访问 或 字典按键访问
a[1]
:__getitem__
- 把对象视作list或dict来对集合赋值:
__setitem__
- 删除元素
del xxx
:__delitem__
getattr()
:__getattr__
- 只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。
- 实现链式调用
- 调用:
__call__
- 当我们调用实例方法时,不用
instance.method()
来调用,直接在实例本身上调用。 - 通过
callable()
:判断一个对象是否是“可调用”对象。
- 当我们调用实例方法时,不用
class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.
- 参考:定制类-廖雪峰
详解装饰器
- 本质:装饰器实际上就是一个函数,因为:
- 参数是一个函数
- 返回值也是一个函数
函数装饰器
无参数
from functools import wraps
def my_log(fun):
@wraps(func)
def wrapper(*args, **kwargs):
print('hello world')
return func(*args, **kwargs) # 原函数可能有返回值
return wrapper
@my_log # 等同于 run = my_log(run),即 run = wrapper。
def run():
print('run')
@my_log
def add(a, b):
print(u'结果是:%s' % (a+b))
run()
# run.__name__代表的是 run这个函数的名称
print(run.__name__)
add(1, 2)
有参数
# 有参数
def my_log(param):
def decorator(fun):
@wraps(func)
def wrapper(*args, **kwargs):
print(param)
print('hello world')
return func(*args, **kwargs) # 原函数可能有返回值
return wrapper
return decorator
@my_log("参数") # 等同于 add = decorator,也就是 add = decorator(add),即 add = wrapper?????????
def add(a, b):
print(u'结果是:%s' % (a+b))
add(1, 2)
- 装饰器的使用是:@符号,放在函数之前。
*args, **kwargs
:位置传参 与 关键字传参 组合起来即可表示任何参数。解决传参时参数不确定的问题。@wraps(func)
保证了函数相关的__name__ 等私有属性不丢失。- 通过self获取原函数的实例,从而调用对应实例的一些方法
类装饰器
from functools import wraps
class DecoratorClass(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print("装饰器!!!")
self.notify() # 类的方法
return func(*args, **kwargs)
return wrapper
def notify(self):
print('notify')
@DecoratorClass() # 可以传参
def myfunc():
print('myfunc')
myfunc()
进程间通信方式
- 文件
- 管道(pipe)、消息队列(queue)、共享内存、信号()、套接字(socket)
并发与并行
- 并发是指一个处理器同时处理多个任务,并发是逻辑上的同时发生(微观并非同时进行,宏观是同时发生的)。
- 并行是指多个处理器或者是多核的处理器同时处理多个不同的任务,并行是物理上的同时处理。
异步与同步
- 异步:多任务, 多个任务之间执行没有先后顺序,可以同时运行,执行的先后顺序不会有什么影响,存在的多条运行主线。
- 同步:多任务, 多个任务之间执行的时候要求有先后顺序,必须一个先执行完成之后,另一个才能继续执行, 只有一个主线。(给用户一种卡死的假象)
同步与互斥
- 同步与互斥&同步与异步的区别
- 同步与互斥 是针对计算机资源。
- 同步与异步 是指编程思想。
- 临界资源:对多个进程或者线程都可见的,容易产生争夺的资源,这类资源被称为临界资源。
- 临界区:对临界资源进行操作的代码区域称之为临界区。
- 解决资源争夺的方案:同步或者互斥
- 同步:同步是一种合作关系,为完成某种任务而建立的多个进程或者线程之间的协调调用,次序等待,传递消息告知资源占用情况。
- 互斥:互斥是一种制约关系,当一个进程或者线程进入到临界区后,会进行加锁操作,此时其他进程或线程无法进入临界区,只有当该进程或线程使用并解锁后,其他进程或线程才可以使用。这种技术往往是通过阻塞完成。
生成器与迭代器
- 迭代器是指用iter(obj)函数返回的对象(实例),迭代器可以用next(it) 函数获取可迭代对象的数据。
- 生成器能够动态提供数据对象,生成器对象也是可迭代对象。含有yield 语句的函数是生成器函数,此函数被调用时返回一个生成器对象。
Python的GC垃圾回收机制
Asycio 与 协程
源码解析:Python Asyncio调度原理
python推荐的第三方aio库:asyncio/wiki/ThirdParty
-
核心是EventLoop调度:run_forever 事件循环能一直执行着,把事件注册到待执行的事件中,EventLoop逐一执行,执行事件的时候就是把控制权交给了被执行的事件,待事件交出执行权后,EventLoop再执行下一个事件。
-
官方没有提供异步的文件io,建议使用aiofiles。
-
python的协程是用户态线程,在用户空间完成并发的一种方式。
-
多线程的创建销毁、切换上下文开销较大,协程没有内核态线程切换的开销。
-
多线程是抢占的,python协程是一种非抢占调度,使用时要注意被某个协程阻塞的问题。
-
协程在同一个线程执行,理论上不需要锁机制,但是实际开发中如果交出了控制权就会引起临界资源的并发问题。
-
个人认为:Python协程能绕开GIL对多线程并发能力的限制,但是使用时需要注意协程中同步代码对协程的阻塞影响,io部分需要尽量使用异步io。
-
Python协程只能用在io密集型场景有性能提升,对CPU密集型场景无用。
-
协程异步:
- Asyncio.queue:可以同步地put,方便改造同步库来使用异步io。
- 如aio-statsd-client:queue同步put_nowait,后台协程await get,使用异步io库发送udp。
- Asyncio.Lock:避免协程临界资源发生竞争。
- Asyncio.queue:可以同步地put,方便改造同步库来使用异步io。
-
python3.5 引入asyn and await语法,替代yield。
WSGI 和 ASGI
-
WSGI是:同步python web 框架的规范,用来接收请求再分发给python web服务器。
- Flask和Django是同步框架,搭配gunicorn和uwsgi部署,接收客户端请求再分发给不同的Flask或Django进程实现并发和并行。
-
ASGI是异步io框架规范,Sanic内置了一个 Web 服务器,所以无需再使用gunicorn/uWSGI部署。
-
常见的WSGI部署方式:
- 异步web框架,可搭配 ASGI 服务器 或者 Gunicorn。
- 异步web框架,可搭配 ASGI 服务器 或者 Gunicorn。
Python进程间通信 和 线程间通信
线程间通信
Python 并发编程(04):详解线程间通信的三种方法(Event、Condition、Queue)
官方文档:threading — 基于线程的并行
- 队列:
from queue import Queue
。 - 全局变量共享:注意临界资源并发安全。
- Event事件:
Threading.Event
,多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。 - Condition:和Event 是类似。
进程间通信
- 常见的方式:管道,消息队列,共享内存,信号,本地套接字。
管道
multiprocessing.pipe
,在内存中开辟一个管道空间,对多个进程可见。在通信形式上形成一种约束。- 默认是双工,可以配置成单工管道。
- send是非阻塞的,recv是阻塞。
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
print(conn.recv()) # prints "hello from parent"
# conn.close()
return
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'hello']"
parent_conn.send("hello from parent")
p.join()
消息队列
multiprocessing.Queue
,在内存中开辟一个消息队列模型,用来存放消息。任何拥有队列对象的进程都可以进行消息的存放。
from multiprocessing import Process, Queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,))
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
共享内存
- 注意处理python的数据类型与C语言的数据类型的转换。
from multiprocessing import Process, Value, Array
def f(n, a):
n.value = 3.1415927
for i in range(len(a)):
a[i] = -a[i]
if __name__ == '__main__':
num = Value('d', 0.0)
arr = Array('i', range(10))
p = Process(target=f, args=(num, arr))
p.start()
p.join()
print(num.value)
print(arr[:])
multiprocessing.shared_memory(可跨进程直接访问的共享内存)
- multiprocessing.shared_memory — 可跨进程直接访问的共享内存
shared_memory.ShareableList
:跨进程分享的List。
信号
- 信号是唯一一种异步的进程间通信方式。
# 自定义函数处理信号
import signal, time, os
# 自定义函数有固定的参数格式,参数为信号和信号栈
def handler(sig, frame):
if sig == signal.SIGALRM:
print("收到了时钟信号")
elif sig == signal.SIGINT:
print("收到Ctrl+C信号")
signal.alarm(2) # 定时发出信号
# 通过自定义函数处理alarm函数的信号
signal.signal(signal.SIGALRM, handler)
signal.signal(signal.SIGINT, handler)
while True:
print("waiting for signal")
time.sleep(2)
- 常见信号: