函数----来源于cookbook第七章
记录第七章中比较有意思的部分
只接受关键词参数的函数
关键词参数放在*打头的参数后面或者放在一个单独的*后面即可
1 2 3 4 5 6 7 8 9 10
| def func(x, *args, y): ...
def func(x, *, y): ...
func(10, y = 20) func(10, 20)
|
默认参数的问题
python函数的默认参数只会被初始化一次,所以只能赋值给不可变对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| _no_value = object()
def func(a, b = None): if b is _no_value: b = [] ...
def func(l = []): l.append(1) print(l)
func() func()
|
匿名函数的一些问题
匿名函数即lambda函数,请时刻记住匿名函数是懒惰求值的。
要固定下匿名函数中使用的参数,必须在匿名函数参数列表中确定。
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 27
| 示例一 懒惰求值 x = 10 y = 1 a = lambda x:x+y print(a(20)) y = 2 print(a(20))
x = 10 y = 1 a = lambda x, y=y:x+y print(a(20)) y = 2 print(a(20))
funs = [lambda x:x+n for n in range(5)] for f in funs: print(f(0))
funs = [lambda x,n=n:x+n for n in range(5)] for f in funs: print(f(0))
|
固定多个参数的函数的某些参数
可以使用functools
中的partial()
工厂方法来实现。
1 2 3 4 5 6 7 8 9 10 11 12
| from functools import partial
def func(a,b,c,d): print(a,b,c,d)
func2 = partial(func, 1) func3 = partial(func, 1, 2, d =4)
func2(-2,-3,-4) func3(-3)
|
1 2 3 4 5 6 7 8 9 10 11
| points = [(1,2),(3,4)]
import math from functools import partial
def distance(p1,p2): return math.hypot(p1[0]-p2[0],p1[1]-p2[1])
points.sort(key = partial(distance,(0,0)))
|
闭包与单个函数的类
可以用闭包的工厂方法来代替只有单个函数的类。因为引入类的作用就是为了保存在类内可以使用的固定参数。
1 2 3 4 5 6 7 8 9
| def template(template): def opener(*args): return template.format(args) return opener
func = template("{} is {}") func(args)
|
闭包,但是可以修改参数
上述的闭包只是将传递的参数作为常量来使用,现在我们需要修改它,比如计数功能。
只需要在内层的函数内使用nonlocal
关键字即可
1 2 3 4 5 6 7 8 9 10 11 12 13
| def handle(): seq = 0 def inner_handle(result): nonlocal seq seq+=1 print(f"{seq} : {result}") return inner_handle
h = handle() h("first") h("second")
|
协程与回调函数
可以利用yield关键字中断发送和接受数据的方式来构造协程。
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
def handle(): seq = 0 while True: result = yield seq += 1 print(f"{seq} : {result}")
h = handle() next(h) h.send("first") h.send("second")
from queue import Queue from functools import wraps
def apply_async(func, args, *, callback): result = func(*args) callback(result)
class Async: def __init__(self, func, args): self.func = func self.args = args
def inline_async(func): @wraps(func) def warpper(*args): f = func(*args) print(f) result_queue = Queue() result_queue.put(None) while True: result = result_queue.get() try: a = f.send(result) apply_async(a.func, a.args, callback = result_queue.put) except StopIteration: break return warpper
def add(x,y): return x+y
@inline_async def test(): r = yield Async(add,(2,3)) print(r) r = yield Async(add,("hello ", "world")) print(r)
|
这个例子感觉有些复杂,但其实它使用装饰器后可以简化在业务逻辑(test()函数)中处理回调函数(不然就得写成apply_async(add,(2,3),callback
= ...)这种东西,需要手动去管理回调函数)
实现的原理是利用yield来获取到调用的函数和参数(被通过一个类封装了),然后执行计算、回调等任务,再使用send方法把这个值发送到业务逻辑中。
访问在闭包内的变量
一般来说闭包内的变量不太好被外界给访问和修改,但是我们可以创建存取函数来实现。
因为python中一切皆对象,所以闭包就可以当作轻量级的类来用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| def closure(): n = 0
def func(): print(f"n = {n}")
def get_(): return n
def set_(value): nonlocal n n = value
func.getn = get_ func.setn = set_ return func
f = closure() f() f.get() f.set(10) f.get()
|
这个结构基本上就是一个类了,但是其运行速度会更快,更加轻量级。