函数
一、编写可接受任意数量参数函数
可接受任意数量的位置参数的函数,可以使用以*开头的参数。
接受任意数量的关键字参数,可以使用以**开头的参数。
在函数定义中,以*打头的参数只能作为最后一个位置参数出现,而以**打头的参数只能作为最后一个参数出现。
在*打头的参数后仍然可以有其他的参数出现。
def a(x, *args, y): pass def a(x, *args, y, **kwargs): pass
这样的参数称之为keyword-only参数。即(出现在*args之后的参数只能作为关键字参数使用)。
三、将元数据信息附加到函数参数上
为参数附加上一些额外的信息,函数的参数注解可以提示程序员该函数应该如何使用,这是很有帮助的。
def a(x:int, y:int) -> int : return x + y >>> help(a) Help on function a in module __main__: a(x:int, y:int) -> int
函数注解只会保存在函数的__annotations__属性中。
五、定义带有默认参数的函数
如果默认值是可变容器的话,比如说列表、集合或者字典,那么应该把None作为默认值。
def a(a, b=None): if b is None: b = [] ...
对默认参数的赋值只会在函数定义的时候绑定一次。
>>> x = 42 >>> def spam(a,b=x): ... print(a,b) ... >>> >>> spam(1) 1 42 >>> x = 11 >>> spam(1) 1 42
修改变量x的值并没有对函数产生任何效果。
这是因为默认值已经在函数定义的时候就确定好了。
>>> def spam(a, b=[]): ... print(b) ... return b ... >>> >>> >>> x = spam(1) [] >>> x.append('Heoo') >>> x.append(888) >>> x ['Heoo', 888] >>> spam(32) ['Heoo', 888]
如果默认值在函数体之外被修改了,那么这种修改将在之后的函数调用中种对参数的默认值产生持续的影响。
▲ 给默认参数赋值的应该总是不可变的对象,比如:None,True,False,数字或者字符串。
七、在匿名函数中绑定变量的值
>>> x = 10
>>> a = lambda y: x + y
>>> x = 20
lambda表达式中用到的x是一个自由变量,在运行时才进行绑定而不是定义的时候绑定。
因此,lambda表达式中x的值应该是在执行时确定的,执行时x的值是多少就是多少。
如果需要匿名函数在定义的时候绑定变量,并保持值不变,那么将那个值作为默认参数实现。
>>> a = lambda y, x=x : x + y
>>> func = [lambda x:x+n for n in range(5)] >>> for f in func: ... print(f(0)) # 4 4 4 4 4 >>> func = [lambda x, n=n: x+n for n in range(5)] >>> for f in func: ... print(f(0)) # 0 1 2 3 4
八、带有N个参数的可调用对象以较少的参数形式调用
减少函数的参数数量,应该是用functools.partial()。
函数partial()允许我们给一个或多个参数指定固定的值,以此减少需要提供给之后调用的参数数量。
def spam(a, b, c, d): print(a, b, c, d) >>> from functools import partial >>> s1 = partial(spam, 1, 2, d=42) >>> s1(9) 1 2 9 42
可以给特定的类的__init__方法注入参数。
还可以通过lambda表达式来替代partial()。
>>> z = lambda x : spam(1, 2, x, d=42)
九、用函数替代只有单个方法的类
只有单个方法的类可以通过闭包将其转换成函数。
使用只有单个方法的类唯一原因就是保存额外的状态给类方法使用。
闭包就是一个函数,但是它还保存着额外的变量环境,使得这些变量可以再函数中使用。
闭包的核心特性就是它可以记住定义闭包时的环境。
十、在回调函数中携带额外的状态
def apply_async(func, args, *, callback): result = func() callback(result) def print_result(result): print('Got:',result) def add(x, y): return x + y >>> apply_async(add, (2, 3) callback=print_result) Got : 5
希望回调函数可以同其他变量或者部分环境进行交互时。在回调函数中携带额外信息的方法是:
(1)使用绑定方法(bound-method)而不是普通的函数。
class ResultHandler: def __init__(self): self.sequence = 0 def handler(self,result): self.sequence += 1 print('[{}] Got: {}'.format(self.sequence, result)) >>> r = ResultHandler() >>> apply_async(print_result, (2, 3), callback=r.handler) [1] Got : 5
(2)使用闭包函数
def make_handler(): sequence = 0 def handler(result): nonlocal sequence sequence += 1 print('[{}] Got: {}'.format(sequence, result)) return handler
(3)使用协程,对于协程来说可以使用send()方法作为回调函数。
def make_handler(): sequence = 0 while True: result = yield sequence += 1 print('[{}] Got: {}'.format(sequence, result)) >>> handler = make_handler() >>> next(handler) >>> apply_async(add, (2, 3), callback=handler.send) [1] Got: 5
(4)使用partial()来处理参数个数的问题
class SequenceNo: def __init__(self): self.sequence = 0 def handler(result,seq): seq.sequence += 1 print('[{}] Got: {}'.format(seq.sequence, result)) >>> seq = SequenceNo() >>> from functiools impor partial >>> apply_async(add, (2, 3), callback=partial(handler, seq=seq)) [1] Got:5 >>> apply_async(add, (2, 3), callback=lambda r: handler(r, seq)) [1] Got:5
十一、内联回调函数
将精巧的控制流隐藏在生成器函数之后,例如contextlib模块中的@contextmanager装饰器,将上下文管理器的入口和出口点通过一个yield语句粘合在一起
def apply_async(func, args, *, callback): result = func(*args) callback(result) from queue import Queue from functools import wraps class Async: def __init__(self, func, args): self.func = func self.args = args def inlined_async(func): @wraps(func) def wrapper(*args): f = func(*args) 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 wrapper def add(x, y): return x + y @inlined_async def test(): r = yield Async(add, (2, 3)) print(r) r = yield Async(add, ('hello', 'world')) print(r) for n in range(10): r = yield Async(add, (n, n)) print(r) print('GoodBye') test()
十二、访问定义在闭包内的变量
nonlocal声明使得编写函数来修改内层变量成为可能。
将内层函数拷贝到一个实例的字典中然后将它返回
import sys class ClosureInstance: def __init__(self, func_locals=None): if func_locals is None: func_locals = sys._getframe(1).f_locals self.__dict__.update((key, value) for key,value in func_locals.items() if callable(value)) def __len__(self): return self.__dict__['__len__']() def Stack(): items = [] def push(item): items.append(item) def pop(): return items.pop() def __len__(): return len(items) return ClosureInstance() s = Stack() s.push(444) s.push(333) s.push(222) s.push(111) print(len(s)) print(s.pop()) print(s.pop())