装饰器之丐版singledispatch
前言
在公众号上看到《一日一技:使用装饰器简化大量 if…elif…代码》,感觉很有意思跑去看了下源代码1,看到文档写着 Like singledispatch() but dispatches by value of the first arg ,记得 singledispatch() 好像在标准库 functools2里,于是故事开始。
singledispatch()
英文不好,参考了大佬的文章3知道了singledispatch是单分派泛型函数。
- 泛型函数 generic function :由多个函数组成的函数,可以根据不同的参数类型决定调用那个函数。
- 单分派,single-dispatch:一种泛型函数分派形式,其中实现是根据单个参数的类型选择的。
所以,单分派泛型函数就是根据函数的第一个参数类型决定使用哪个函数的泛型函数。
丐版实现
看看就好,反正真正用的时候 from functools import singledispatch 就行。
from functools import update_wrapper
from collections import defaultdict
def singledispatch(func):
registry = defaultdict(lambda :func)
def dispatch(cls):
return registry[cls]
def register(cls, func=None):
if func is None:
return lambda x: register(cls, x)
registry[cls] = func
return func
def wrapper(*args, **kwargs):
if not args:
raise TypeError('我要根据单个参数选择分派,然后你不给我参数?')
return dispatch(args[0].__class__)(*args, **kwargs)
wrapper.dispatch = dispatch
wrapper.register = register
update_wrapper(wrapper, func)
return wrapper
@singledispatch
def Foo(arg, *args):
print(arg)
@Foo.register(int)
def _1(arg, *args):
print(f'int - {arg}')
# Foo.register(int, _1)
# @Foo.register(list)
def _2(arg, *args):
print(f'list - {arg}')
Foo.register(list, _2)
Foo(3)
Foo([*range(10)])
Foo((1,2))
# int - 3
# list - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# (1, 2)
因为不像标准库里那样,为了一个 best matching implementation from registry for type cls
,去继承里用C3去找,我这里直接偷懒用了一个 defaultdict ,把原函数当成默认的处理函数,匹配不上就用这个而不是去寻找。
然后注册的时候用到 register 函数,可以显式调用或者作为带参数的装饰器,所以实现里面要加一个判断。
总结
装饰器这个东西总能整出新活,一阵子不用就跟忘掉一样,需要长时间的积累和回顾。