《Python3学习笔记》读书笔记(二)
闭包、迭代器、生成器、模式
《Python3学习笔记》读书笔记(二)
闭包
闭包共享自由变量(P160)
- 返回的多个闭包函数可以共享同一自由变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
def queue():
data = []
push = lambda x:data.append(x)
pop = lambda :data.pop(0) if data else None
return push,pop
push,pop = queue()
print(push.__closure__ is push.__closure__) # True
for i in range(3):
push(i)
x = pop()
print(f'{x = }')
延迟绑定(P162)
- 闭包只是绑定自由变量,并不会立即计算引用内容。只有当闭包函数执行时,才能访问所引用的目标对象。
1
2
3
4
5
6
7
8
9
10
11
# late binding
def make(n):
x = []
for i in range(n):
x.append(lambda:print(i))
return x
a, b, c = make(3)
a() # 2
b() # 2
c() # 2
修改策略是将
lamba:print(i)修改为lambda o=i:print(o)。
迭代器
自定义迭代(P175)
- 可迭代对象要实现
__iter__方法,该方法返回迭代器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Data:
def __init__(self,n):
self.data = list(range(n))
def __iter__(self):
return DataIter(self.data) # 返回迭代器实例
class DataIter:
def __init__(self,data):
self.data = data
self.index = 0
def __next__(self):
if not self.data or self.index>= len(self.data):
raise StopIteration
d = self.data[self.index]
self.index += 1
return d
def __iter__(self):
return self
x = Data(2).__iter__()
print(x.__next__()) # 0
print(x.__next__()) # 1
iter 函数(P177)
iter函数可以为实现__getitem__的序列对象自动创建迭代器包装。
1
2
3
4
5
6
7
8
9
10
11
12
class Data:
def __init__(self,n):
self.n = n
def __getitem__(self,index):
if index<0 or index>=self.n :
raise IndexError
return index + 100
d = Data(2)
for x in iter(d): # 可省略 iter
print(x) # 100 101
iter甚至可以对函数、方法等 callable 进行包装。
1
2
3
4
x = lambda :input("n :")
for i in iter(x,'end'): # 输入 end 结束
print(f'输入的是',i)
生成器
yield from(P180)
- 若数据源本身是可迭代对象,可以使用
yield from直接从子迭代语句返回数据。
1
2
3
4
5
6
def test():
yield from 'abc'
yield from range(5)
for i in test():
print(i)
生成器双向通信(P183)
- 使用
iterator.send()方法向迭代器发送数据。 - 使用
iterator.throw()方法向迭代器发送异常以结束迭代。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ExitException(Exception):pass
class ResetException(Exception):pass
def test():
while True:
try:
v = yield
print(f'recv:{v = }')
except ResetException:
print('reset...')
except ExitException:
print('exit...')
return
x = test()
x.send(None) # 启动生成器
x.send(1)
x.send(2)
x.throw(ResetException)
x.send(3)
x.send(4)
x.throw(ExitException)
模式
生产者消费者(P186)
- 首先启动消费者,消费者调用
yield将执行权限让给生产者 - 生产者生产数据后,通过
send发送给消费者 - 生产者可以
throw异常给消费者通知数据已生产完毕
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def consumer():
while True:
v = yield # 将执行权限交给生产者
print(f'comsume: {v}')
def producer(c):
for i in range(10,13):
c.send(i) # 向消费者发送数据使其消费
c = consumer() # 创建消费者
c.send(None) # 启动消费者
producer(c)
c.close() # 关闭消费者
消除回调(P187)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import time,threading
def wrapper(worker):
try:
s = time.time()
g = worker()
result = g.send(None) # 启动生成器任务
print(f'{result = }') # result 是 worker 执行结果
time.sleep(2)
g.send(f'done: {time.time() - s}') # 此次 send 导致 StopIteration
except StopIteration:
print('StopIteration')
def service(worker):
thread = threading.Thread(target = wrapper,args = (worker,))
thread.start()
# worker 函数必须是生成器函数
def worker():
print('start...')
x = yield('in job')
print(x)
service(worker)
协程(P189)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from functools import partial
import time,random
def sched(*tasks):
tasks = list(map(lambda t:t(),tasks))
while tasks:
try:
task = tasks.pop(0) # 从列表头部弹出任务
task.send(None) # 启动任务
tasks.append(task) # 任务未结束,放在列表末尾
except StopIteration:
pass
def task(id,n,m):
for i in range(n,m):
print(f"{id}:{i}")
yield # 主动调度
time.sleep(random.random()) # 休眠依旧占据CPU?
t1 = partial(task,1,10,13)
t2 = partial(task,2,30,33)
sched(t1,t2)
无源码部署(P203)
1
2
3
4
5
# 将当前文件夹下所有文件编译为字节码
python -m compileall -b -o 2 .
# 运行
python main.pyc
动态导入
方法一(P208)
- 使用
exec动态执行,随后从sys.modules中获取模块实例
1
2
3
4
5
6
7
8
import sys
def LoadModule(name):
exec(f'import {name}')
return sys.modules[name]
math = LoadModule('math')
print(math.pi)
方法二(P209)
- 使用
importlib库
1
2
3
4
5
6
7
import importlib
def LoadModule(name):
return importlib.import_module(name)
math = LoadModule('math')
print(math.pi)
本文由作者按照 CC BY 4.0 进行授权