How can I use functools.singledispatch with instance methods?
python 3.4增加了用静态方法定义函数重载的能力。这基本上是文档中的示例:
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 | from functools import singledispatch class TestClass(object): @singledispatch def test_method(arg, verbose=False): if verbose: print("Let me just say,", end="") print(arg) @test_method.register(int) def _(arg): print("Strength in numbers, eh?", end="") print(arg) @test_method.register(list) def _(arg): print("Enumerate this:") for i, elem in enumerate(arg): print(i, elem) if __name__ == '__main__': TestClass.test_method(55555) TestClass.test_method([33, 22, 11]) |
在最纯粹的形式中,
对于如何使用(或Jerry Rig)此功能使其与实例方法一起工作,是否有人有任何建议?
从
1 2 | def wrapper(*args, **kw): return dispatch(args[0].__class__)(*args, **kw) |
…对于一个正则函数来说是可以的,但是对于一个实例方法却没有太大的用处,它的第一个参数总是
但是,我们可以编写一个新的decorator
1 2 3 4 5 6 7 8 9 | from functools import singledispatch, update_wrapper def methdispatch(func): dispatcher = singledispatch(func) def wrapper(*args, **kw): return dispatcher.dispatch(args[1].__class__)(*args, **kw) wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper |
下面是正在使用的装饰器的一个简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Patchwork(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) @methdispatch def get(self, arg): return getattr(self, arg, None) @get.register(list) def _(self, arg): return [self.get(x) for x in arg] |
请注意,装饰的
测试
1 2 3 4 5 | >>> pw = Patchwork(a=1, b=2, c=3) >>> pw.get("b") 2 >>> pw.get(["a","c"]) [1, 3] |
decorator本质上是一个包装器,它将包装函数作为参数并返回另一个函数。
如接受的答案所述,
如该答案所示,在这种情况下,您可以编写另一个包装器来monkey修补这个装饰器。但这种黑客修复并不总是最好的选择。
与其他任何函数一样,您可以调用包装器并显式地将参数传递给它,如果这种方法重载很少在包中进行,那么它对我来说似乎更简单、更平和易读。
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 | from functools import singledispatch class TestClass(object): def __init__(self): self.test_method = singledispatch(self.test_method) self.test_method.register(int, self._test_method_int) self.test_method.register(list, self._test_method_list) def test_method(self, arg, verbose=False): if verbose: print("Let me just say,", end="") print(arg) def _test_method_int(self, arg): print("Strength in numbers, eh?", end="") print(arg) def _test_method_list(self, arg): print("Enumerate this:") for i, elem in enumerate(arg): print(i, elem) if __name__ == '__main__': test = TestClass() test.test_method(55555) test.test_method([33, 22, 11]) |
还有另一个模块,
除了
The dispatch decorator uses the name of the function to select the
appropriate Dispatcher object to which it adds the new
signature/function. When it encounters a new function name it creates
a new Dispatcher object and stores name/Dispatcher pair in a namespace
for future reference.
例如:
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 | from types import LambdaType from multipledispatch import dispatch class TestClass(object): @dispatch(object) def test_method(self, arg, verbose=False): if verbose: print("Let me just say,", end="") print(arg) @dispatch(int, float) def test_method(self, arg, arg2): print("Strength in numbers, eh?", end="") print(arg + arg2) @dispatch((list, tuple), LambdaType, type) def test_method(self, arg, arg2, arg3): print("Enumerate this:") for i, elem in enumerate(arg): print(i, arg3(arg2(elem))) if __name__ == '__main__': test = TestClass() test.test_method(55555, 9.5) test.test_method([33, 22, 11], lambda x: x*2, float) |