函数装饰器和闭包(四)
上一章:函数装饰器和闭包(三)
单分派函数
假设我们现在要开发一个函数,这个函数可以传入一个元素,函数要判断元素的类型,再将其打印出来
from collections.abc import MutableSequence, MutableMapping def print_item(item): if isinstance(item, int): print("int:", item) elif isinstance(item, MutableSequence): print("iter:", item) elif isinstance(item, MutableMapping): print("map:", item) else: print(item)
运行结果:
>>> print_item(1) int: 1 >>> print_item([1, 2, 3]) sequence: [1, 2, 3] >>> print_item({"a": 1, "b": 2}) map: {'a': 1, 'b': 2}
这个函数很好理解,没有什么好解释的,但是我们要通过这个函数来展示如何使用functools.singledispatch装饰器让Python实现“重载”的效果
from collections.abc import MutableSequence, MutableMapping from functools import singledispatch @singledispatch # <1> def print_item(item): print(item) @print_item.register(int) # <2> @print_item.register(float) def _(item): print("number:", item) @print_item.register(str) # <3> def _(item): print("str:", item) @print_item.register(MutableSequence) # <4> def _(item): print("sequence:", item) @print_item.register(MutableMapping) # <5> def _(item): print("map:", item)
我们用@singledispatch装饰了print_item函数,把这个函数作为基函数,然后再用@print_item.register装饰其他函数,当我们调用print_item,会根据item的类型查找应该执行的函数
- @singledispatch标记处理object类型的基函数
- 当传入参数为int类型进入此方法,这个时候专门的函数名称已经无关紧要了,我们用_代替,同时可以用多个@print_item.register装饰同一个函数
- 当传入参数为str类型进入此方法
- 当传入参数为序列类型进入此方法
- 当传入参数为字典类型进入此方法
运行结果:
>>> print_item(1) number: 1 >>> print_item(1.1) number: 1.1 >>> print_item("hello") str: hello >>> print_item([1, 2, 3]) sequence: [1, 2, 3] >>> print_item({"a": 1, "b": 2}) map: {'a': 1, 'b': 2}
参数化装饰器
Python在解释装饰器时,把被装饰的函数当做第一个参数传递给装饰器函数,但怎么样才能让装饰器接收其他参数呢?答案是:创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰的函数上。可能这样的解释听得云里雾里,没关系,我们来看下面一个例子
registry = set() # <1> def register(active=True): # <2> def decorate(func): # <3> print('running register(active=%s)->decorate(%s)' % (active, func)) if active: # <4> registry.add(func) else: registry.discard(func) # <5> return func # <6> return decorate # <7> @register(active=False) # <8> def f1(): print('running f1()') @register() # <9> def f2(): print('running f2()') def f3(): print('running f3()')
- 声明一个名为registry的集合对象
- register方法接收一个可选的关键字参数
- decorate这个内部函数才是真正的装饰器,注意它只有一个参数,而且接收的是函数
- 只有active的值为真,才将func注册到registry集合中
- 如果active的值不为真,则将func从registry集合删除
- decorate是装饰器,必须返回一个函数
- register是装饰器工厂函数,因此返回decorate
- @register工厂函数必须作为函数调用,并且传入所需的参数
- 即时不传入参数,register也必须作为函数调用
我们导入这个文件:
>>> from registration_param import * running register(active=False)->decorate(<function f1 at 0x00000072D8DA4620>) running register(active=True)->decorate(<function f2 at 0x00000072D8DA46A8>) >>> registry {<function f2 at 0x00000072D8DA46A8>}
可以看到,当把register作为装饰器工厂函数,返回的decorate函数得到了执行,由于f1传入的active为False,所以f1没有注册到registry集合对象里面,只有f2被注册到集合对象,我们打印registry,可以看到,里面确实只有f2函数对象