Dus*_*rea 40 python single-dispatch instance-method python-3.x python-3.4
Python 3.4 增加了使用静态方法定义函数重载的功能.这基本上是文档中的示例:
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])
Run Code Online (Sandbox Code Playgroud)
在其最纯粹的形式中,singledispatch实现依赖于第一个用于标识类型的参数,因此将此功能扩展到实例方法变得棘手.
有没有人有任何关于如何使用(或jerry-rig)此功能以使其与实例方法一起使用的建议?
Zer*_*eus 62
纵观来源的functools.singledispatchmethod,我们可以看到,装饰返回一个函数singledispatch,它选择的功能,从那些基于类型注册的呼叫wrapper()...
def wrapper(*args, **kw):
return dispatch(args[0].__class__)(*args, **kw)
Run Code Online (Sandbox Code Playgroud)
...对于常规函数来说很好,但对实例方法没什么用处,它的第一个参数总是如此args[0].
但是,我们可以编写一个新的装饰器self,它依赖于methdispatch繁重的工作,而是返回一个包装函数,它根据以下类型选择要调用的注册函数singledispatch:
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
Run Code Online (Sandbox Code Playgroud)
这是一个使用装饰器的简单示例:
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]
Run Code Online (Sandbox Code Playgroud)
请注意,装饰args[1]方法和注册的方法get()都list像往常一样具有初始参数.
测试self课程:
>>> pw = Patchwork(a=1, b=2, c=3)
>>> pw.get("b")
2
>>> pw.get(["a", "c"])
[1, 3]
Run Code Online (Sandbox Code Playgroud)
Nun*_*dré 11
装饰器本质上是一个包装器,它将包装函数作为参数并返回另一个函数.
如接受的答案中所述,singledispatch返回a wrapper将第一个参数作为已注册的类型 - self在实例方法中.
如该答案所示,在这种情况下,您可以编写另一个封装器来修补装饰器.但是这种hacky修复并不总是最好的选择.
与任何其他函数一样,您可以调用包装器并将参数显式传递给它,如果这种方法重载很少在包中进行,这对我来说似乎更简单,更平坦,更易读.
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])
Run Code Online (Sandbox Code Playgroud)
还有另一个模块,multipledispatch(不是标准的,但包含在Anaconda中,没有任何非标准的依赖关系),正如名称已经表明的那样singledispatch,并且不同于允许多方法.
除了Dispatcher具有singledispatch-compatible syntaxis的对象之外,它还提供了一个dispatch装饰器,它隐藏了用户对这些对象的创建和操作.
调度装饰器使用函数的名称来选择添加新签名/函数的相应Dispatcher对象.当遇到新的函数名时,它会创建一个新的Dispatcher对象,并将名称/ Dispatcher对存储在命名空间中以供将来参考.
例如:
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)
Run Code Online (Sandbox Code Playgroud)
小智 6
Python 3.8 functools 使用新的装饰器为实例方法引入了函数重载@singledispatchmethod。
根据文档;
分派发生在第一个非 self或非cls参数的类型上。
因此,type紧随其后的 for 参数self是触发方法分派的参数。像下面这样的东西。
from functools import singledispatchmethod
class Cooking:
@singledispatchmethod
def cook(self, arg):
return f"I'm cooking {arg} eggs."
@cook.register
def _(self, arg: int):
return f"I'm cooking {arg} eggs."
@cook.register
def _(self, arg: bool):
return f"Am I cooking eggs? {arg}"
f = Cooking()
print(f.cook('many'))
# I'm cooking many eggs.
print(f.cook(50))
# I'm cooking 50 eggs.
print(f.cook(True))
# Am I cooking eggs? True
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8770 次 |
| 最近记录: |