Python - Debug + Profile
pdb
pdb 是 Python 自带的调试库,为 Python 程序提供交互式的源代码调试功能,是命令行版本的 IDE 断点调试器
Instruction
Desc
p
print
n
next - step over
l
list - show source code context
s
step into
r
stop out - 继续执行,直到当前函数完成后返回
`b [ ([filename:]lineno
function) [, condition] ]`
c
continue - 一直执行程序,直到遇到下一个断点
step into - --Call-- + --Return--
cProfile
瓶颈在于 fib 函数
Item
Desc
ncalls
相应代码/函数被调用的次数
tottime
相应代码/函数总共执行所需的时间(不包括它调用其它代码/函数的执行时间)
tottime percall
tottime / ncalls
cumtime
相应代 ...
Python - With
场景
资源是有限的,使用过后需要释放,否则会造成资源泄露
File123for x in range(10_000_000): f = open('test.txt', 'w') # OSError: [Errno 24] Too many open files f.write('hello')
context manager 帮助自动分配并释放资源 - with 语句
123for x in range(10_000_000): with open('test.txt', 'w') as f: # This will open and close the file 10_000_000 times f.write('hello')
try - finally
12345f = open('test.txt', 'w')try: f.write('hello')finall ...
Python - GC
计数引用
Python 中一切皆对象,变量的本质是对像的指针
当一个对象的引用计数(指针数)为 0 时,说明该对象不可达,成为垃圾,需要被回收
调用函数 func(),创建列表 a 后,内存占用迅速增加,在函数调用结束后,内存恢复正常
函数内部声明的列表 a 是局部变量,在函数返回后,局部变量的引用会被注销
列表 a 所指向的对象的引用数为 0,Python 会执行 GC,回收内存
全局变量 - global
将生成的列表返回,在主程序中接收
内部实现1234567891011121314import sysa = []print(sys.getrefcount(a)) # 2 = a + getrefcountdef func(a): print(sys.getrefcount(a)) # 4 = a + getrefcount + func call stack + func argsfunc(a)print(sys.getrefcount(a)) # 2 = a + getrefcount
sys.getrefcount 本身也会引入一次计数
在函数调用发生的 ...
Python - GIL
Cpu bound
单线程 - 2.555494917
12345678910111213import timedef CountDown(n): while n > 0: n -= 1start = time.perf_counter()n = 100_000_000CountDown(n)end = time.perf_counter()print(f"Time elapsed: {end - start} seconds") # Time elapsed: 2.555494917 seconds
多线程 - 2.477930167
123456789101112131415161718192021import timefrom threading import Threaddef CountDown(n): while n > 0: n -= 1start = time.perf_counter()n = 100_000_000t1 = Thread(target=CountDown, args=[ ...
Python - Async IO
线程局限
多线程在运行过程中容易被打断,有可能会出现 race condition 的情况
线程切换本身存在开销,不能无限增加线程数
Sync vs Async
Sync - 操作顺序执行,前面阻塞后面
Async - 不同操作间可以交替执行,如果其中一个操作被阻塞了,程序不会等待,而是会找出可执行的操作继续执行
原理
CSP - Communicating sequential processes
asyncio 与 Python 程序一样,都是单线程的
asyncio 只有一个主线程,但可以进行多个不同的任务(特殊的 Future 对象),这些不同的任务被 Event loop 控制
假设任务只有两个状态 - 预备状态 / 等待状态
预备状态 - 任务目前空闲,随时准备运行
等待状态 - 任务已经运行,但在等待外部操作完成(如 IO)
Event loop 维护两个任务列表,分别对应预备状态和等待状态
选取预备状态的一个任务(与任务的等待时长、占用资源等相关),使其运行,直到该任务将控制权交还给 Event loop 为止
当任务将控制权交还给 Event loop 时,Even ...
Python - Futures
并行 vs 并发
并发(Concurrency) - 在某个特定的时刻,只允许有一个操作发生,线程和任务之间会相互切换,交替运行
并行(Parallelism) - 在同一时刻,有多个操作同时进行
Python 中有两种并发形式 - threading + asyncio
threading
操作系统知道每个线程的所有信息,在适当的时候做线程切换
优点 - 代码易于编写,程序员不需要做任何切换操作
缺点 - 容易出现 race condition
asyncio
主程序想要切换任务时,必须得到此任务可以切换的通知
避免了 race condition 的情况
场景
并发通常用于 IO 密集的场景 - Web 应用
并行通常用于 CPU 密集的场景 - MapReduce
线程池 vs 进程池
大部分时间是浪费在 IO 等待上
多线程(并发) - 16.8s -> 3.5s
12with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: executor.map(downloa ...
Python - Coroutine
基础
协程是实现并发编程的一种方式
多线程/多进程模型,是解决并发问题的经典模式
C10K - 线程/进程上下文切换占用大量资源
Nginx Event loop
启动一个统一的调度器,让调度器来决定一个时刻去运行哪个任务
节省了多线程中启动线程、管理线程、同步锁等各种开销
相比于 Apache,用更低的资源支持更多的并发连接
Callback hell - JavaScript
继承了 Event loop 的优越性,同时还提供 async / await 语法糖,解决了执行性和可读性共存的难题
协程开始崭露头角,尝试使用 Node.js 实现后端
Python 3.7 提供基于 asyncio 的 async / await 方法
同步
简单实现
12345678910111213141516import timedef crawl_page(url): print('crawling {}'.format(url)) sleep_time = int(url.split('_ ...
Python - Iterator + Generator
迭代器
Python 中一切皆对象,对象的抽象就是类,对象的集合为容器(列表、元组、字典、集合)
所有的容器都是可迭代的(iterable)
迭代器(iterator)提供了一个 next 的方法
得到容器的下一个对象,或者得到一个 StopIteration 的错误
可迭代对象,通过 iter() 函数返回一个迭代器(iterator),再通过 next() 函数实现遍历
for in 语句隐式化了该迭代过程
判断一个对象是否可迭代 - iter(obj) 或者 isinstance(obj, Iterable)
1234567891011121314151617181920212223from typing import Iterabledef is_iterable(param): try: iter(param) return True except TypeError: return Falseparams = [ 1234, # False '1234', # True [1, 2, ...
Python - Metaclass
超越变形
YAMLObject 的一个超越变形能力,即的任意子类支持序列化和反序列化
123456789101112131415161718192021222324252627282930import yamlclass Monster(yaml.YAMLObject): yaml_tag = u'!Monster' def __init__(self, name, hp, ac, attacks): self.name = name self.hp = hp self.ac = ac self.attacks = attacks def __repr__(self): return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % ( self.__class__.__name__, self.name, self.hp, self.ac, self.attacks)yaml.load("&q ...
Python - Decorator
函数
在 Python 中,函数是一等公民,函数是对象,可以将函数赋予变量
将函数赋值给变量
123456789def func(message): print('Got a message: {}'.format(message))send_message = func # assign the function to a variableprint(type(func)) # <class 'function'>print(type(send_message)) # <class 'function'>send_message('hello world') # call the function
将函数当成函数参数传递给另一个函数
12345678910def get_message(message): return 'Got a message: ' + messagedef root_call(func, message): ...