2021.09.05 - Python重难点总结

多重继承

顺序问题&同名方法

Python菱形继承的初始化问题和继承顺序

问题:如果不同的父类中存在 同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?

  • 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:避免协程临界资源发生竞争。
  • python3.5 引入asyn and await语法,替代yield。

WSGI 和 ASGI

ASGI文档

  • WSGI是:同步python web 框架的规范,用来接收请求再分发给python web服务器。

    • Flask和Django是同步框架,搭配gunicorn和uwsgi部署,接收客户端请求再分发给不同的Flask或Django进程实现并发和并行。
  • ASGI是异步io框架规范,Sanic内置了一个 Web 服务器,所以无需再使用gunicorn/uWSGI部署。

  • 常见的WSGI部署方式:

    • 异步web框架,可搭配 ASGI 服务器 或者 Gunicorn。
      在这里插入图片描述

Python进程间通信 和 线程间通信

线程间通信

Python 并发编程(04):详解线程间通信的三种方法(Event、Condition、Queue)
官方文档:threading — 基于线程的并行

  • 队列:from queue import Queue
  • 全局变量共享:注意临界资源并发安全。
  • Event事件:Threading.Event,多个线程可以等待某个事件的发生,在事件发生后,所有的线程都会被激活。
  • Condition:和Event 是类似。

进程间通信

官方文档:multiprocessing—基于进程的并行¶

  • 常见的方式:管道,消息队列,共享内存,信号,本地套接字。

管道

  • 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(可跨进程直接访问的共享内存)

信号

  • 信号是唯一一种异步的进程间通信方式。
# 自定义函数处理信号
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)
  • 常见信号:
    在这里插入图片描述在这里插入图片描述

本地套接字???

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值