python3:类中的singledispatch,如何调度自类型 [英] python3: singledispatch in class, how to dispatch self type
问题描述
使用python3.4。在这里,我想使用singledispatch在 __ mul __
方法中调度不同的类型。像这样的代码:
Using python3.4. Here I want use singledispatch to dispatch different type in __mul__
method . The code like this :
class Vector(object):
## some code not paste
@functools.singledispatch
def __mul__(self, other):
raise NotImplementedError("can't mul these type")
@__mul__.register(int)
@__mul__.register(object) # Becasue can't use Vector , I have to use object
def _(self, other):
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
@__mul__.register(Vector) # how can I use the self't type
@__mul__.register(object) #
def _(self, other):
pass # need impl
如您所见,我想支持 Vector * Vertor
,这有名称错误
As you can see the code , I want support Vector*Vertor
, This has Name error
Traceback (most recent call last):
File "p_algorithms\vector.py", line 6, in <module>
class Vector(object):
File "p_algorithms\vector.py", line 84, in Vector
@__mul__.register(Vector) # how can I use the self't type
NameError: name 'Vector' is not defined
问题可能是我该如何使用类名类型在类的方法中?我知道c ++有font类声明。 python如何解决我的问题?奇怪的是看到 result = Vector(len(self))
其中 Vector
可以在方法主体中使用
The question may be How can I use class name a Type in the class's method ? I know c++ have font class statement . How python solve my problem ? And it is strange to see result = Vector(len(self))
where the Vector
can be used in method body .
之后,请看 http://lukasz.langa.pl/8/single-dispatch-generic-functions/
我可以选择这种方式来实现:
After have A look at http://lukasz.langa.pl/8/single-dispatch-generic-functions/ I can choose this way to implement :
import unittest
from functools import singledispatch
class Vector(object):
"""Represent a vector in a multidimensional space."""
def __init__(self, d):
self._coords = [0 for i in range(0, d)]
self.__init__mul__()
def __init__mul__(self):
__mul__registry = self.__mul__.registry
self.__mul__ = singledispatch(__mul__registry[object])
self.__mul__.register(int, self.mul_int)
self.__mul__.register(Vector, self.mul_Vector)
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __str__(self):
return str(self._coords)
@singledispatch
def __mul__(self, other):
print ("error type is ", type(other))
print (type(other))
raise NotImplementedError("can't mul these type")
def mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
def mul_Vector(self, other):
print ("other type is ", type(other))
#result = Vector(len(self)) # start with vector of zeros
sum = 0
for i in range(0,len(self)):
sum += self._coords[i] * other._coords[i]
return sum
class TestCase(unittest.TestCase):
def test_singledispatch(self):
# the following demonstrates usage of a few methods
v = Vector(5) # construct five-dimensional <0, 0, 0, 0, 0>
for i in range(1,6):
v[i-1] = i
print(v.__mul__(3))
print(v.__mul__(v))
print(v*3)
if __name__ == "__main__":
unittest.main()
答案很奇怪:
other type is <class 'int'>
[3, 6, 9, 12, 15]
other type is <class '__main__.Vector'>
55
error type is <class 'int'>
Traceback (most recent call last):
File "p_algorithms\vector.py", line 164, in <module>
print(v*3)
File "C:\Python34\lib\functools.py", line 710, in wrapper
return dispatch(args[0].__class__)(*args, **kw)
File "p_algorithms\vector.py", line 111, in __mul__
raise NotImplementedError("can't mul these type")
v .__ mul __(3)
可以工作,但 v * 3
无法工作。这很奇怪从我的选择 v * 3
与 v .__ mul __(3)
相同。
v.__mul__(3)
can work but v*3
can't work. This is strange From my option v*3
is just the same as v.__mul__(3)
.
在@Martijn Pieters评论后更新,我仍然希望实现 v * 3
在班上。所以我试试这个
Update after @Martijn Pieters's comment, I still want implement v*3
in class. So I try this
import unittest
from functools import singledispatch
class Vector(object):
@staticmethod
def static_mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
@singledispatch
@staticmethod
def __static_mul__(cls, other):
print ("error type is ", type(other))
print (type(other))
raise NotImplementedError("can't mul these type")
__mul__registry2 = __static_mul__.registry
__mul__ = singledispatch(__mul__registry2[object])
__mul__.register(int, static_mul_int)
def __init__(self, d):
self._coords = [0 for i in range(0, d)]
self.__init__mul__()
def __init__mul__(self):
__mul__registry = self.__mul__.registry
print ("__mul__registry",__mul__registry,__mul__registry[object])
self.__mul__ = singledispatch(__mul__registry[object])
self.__mul__.register(int, self.mul_int)
print ("at last __mul__registry",self.__mul__.registry)
# @singledispatch
# def __mul__(self, other):
# print ("error type is ", type(other))
# print (type(other))
# raise NotImplementedError("can't mul these type")
def mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __str__(self):
return str(self._coords)
class TestCase(unittest.TestCase):
def test_singledispatch(self):
# the following demonstrates usage of a few methods
v = Vector(5) # construct five-dimensional <0, 0, 0, 0, 0>
for i in range(1,6):
v[i-1] = i
print(v.__mul__(3))
print("type(v).__mul__'s registry:",type(v).__mul__.registry)
type(v).__mul__(v, 3)
print(v*3)
if __name__ == "__main__":
unittest.main()
这次。 v .__ mul __(3)
有错误:
Traceback (most recent call last):
File "test.py", line 73, in test_singledispatch
type(v).__mul__(v, 3)
File "/usr/lib/python3.4/functools.py", line 708, in wrapper
return dispatch(args[0].__class__)(*args, **kw)
TypeError: 'staticmethod' object is not callable
对我而言,静态方法应像实例方法一样工作。
For me static method should act like the instance method.
推荐答案
您不能在方法上使用 functools.singledispatch
全部,至少不作为装饰者。 Python 3.8仅针对方法添加了一个新选项: functools.singledispatchmethod()
。
You cannot use functools.singledispatch
on methods at all, not as a decorator at least. Python 3.8 adds a new option, just for methods: functools.singledispatchmethod()
.
Vector $ c $没关系c>尚未在此处定义;任何方法的第一个参数始终将是
self
,而您在此处将第二个参数使用单调度。
It doesn't matter that Vector
isn't defined here yet; the first argument to any method is always going to be self
, while you'd use single dispatch for the second argument here.
由于装饰器在创建类对象之前应用于函数对象,因此您也可以将方法注册为函数,而不是 ,因此您可以访问 Vector
名称:
Because decorators apply to the function objects before the class object is created, you could just as well register your 'methods' as functions instead, outside of the class body, so you have access to the Vector
name:
class Vector(object):
@functools.singledispatch
def __mul__(self, other):
return NotImplemented
@Vector.__mul__.register(int)
@Vector.__mul__.register(Vector)
def _(self, other):
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
对于不受支持的类型,您需要返回 NotImplemented
singleton ,不提出一个例外。这样,Python也会尝试逆运算。
For non-supported types, you need to return the NotImplemented
singleton, not raise an exception. This way Python will try the inverse operation too.
但是,由于分派将要键入错误的参数( self
),您将必须提出自己的单一调度机制。
However, since the dispatch is going to key on the wrong argument (self
) here anyway, you'll have to come up with your own single dispatch mechanism.
如果您真的想使用 @ functools.singledispatch
您必须将其委托给常规函数,并使用参数 inversed :
If you really want to use @functools.singledispatch
you'd have to delegate to a regular function, with the arguments inversed:
@functools.singledispatch
def _vector_mul(other, self):
return NotImplemented
class Vector(object):
def __mul__(self, other):
return _vector_mul(other, self)
@_vector_mul.register(int)
def _vector_int_mul(other, self):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
关于使用 __ init__mul __
进行的更新: v * 3
被 not 转换为 v .__ mul __(3)
。而是将其翻译为 type(v).__ mul __(v,3)
,请参见 特殊方法查找 。 始终绕过直接在实例上设置的所有方法。
As for your updates using __init__mul__
: v * 3
is not translated to v.__mul__(3)
. It is instead translated to type(v).__mul__(v, 3)
, see Special method lookup in the Python datamodel reference. This always bypasses any methods set directly on the instance.
此处 type(v)
是 Vector
; Python查找 function ,此处将不使用绑定方法。同样,由于 first 参数上的 functools.singledispatch
分派,因此,始终不能直接在的方法上使用单分派Vector
,因为第一个参数始终是 Vector
实例。
Here type(v)
is Vector
; Python looks up the function, it won't use a bound method here. Again, because functools.singledispatch
dispatches on the first argument, always, you cannot use single dispatch directly on the methods of Vector
, because that first argument is always going to be a Vector
instance.
换句话说,Python将不使用您在 __ init__mul __
中的 self
上设置的方法;特殊方法是从不在实例上查找,请参阅 特殊方法查找 在数据模型文档中。
In other words, Python will not use the methods you set on self
in __init__mul__
; special methods are never looked up on the instance, see Special method lookup in the datamodel documentation.
functools.singledispatchmethod()$ Python 3.8添加的c $ c>选项使用 class 作为装饰器,该装饰器实现了描述符协议,就像方法一样。这样,它便可以处理在 绑定之前的分派(因此,在
self
之前要放在参数列表之前),然后绑定 singledispatch
调度程序返回。 此实现的源代码是完全兼容的使用较旧的Python版本,因此您可以改用它:
The functools.singledispatchmethod()
option that Python 3.8 adds uses a class as the decorator which implements the descriptor protocol, just like methods do. This lets it then handle dispatch before binding (so before self
would be prepended to the argument list) and then bind the registered function that the singledispatch
dispatcher returns. The source code for this implementation is fully compatible with older Python versions, so you could use that instead:
from functools import singledispatch, update_wrapper
# Python 3.8 singledispatchmethod, backported
class singledispatchmethod:
"""Single-dispatch generic method descriptor.
Supports wrapping existing descriptors and handles non-descriptor
callables as instance methods.
"""
def __init__(self, func):
if not callable(func) and not hasattr(func, "__get__"):
raise TypeError(f"{func!r} is not callable or a descriptor")
self.dispatcher = singledispatch(func)
self.func = func
def register(self, cls, method=None):
"""generic_method.register(cls, func) -> func
Registers a new implementation for the given *cls* on a *generic_method*.
"""
return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls):
def _method(*args, **kwargs):
method = self.dispatcher.dispatch(args[0].__class__)
return method.__get__(obj, cls)(*args, **kwargs)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method.register = self.register
update_wrapper(_method, self.func)
return _method
@property
def __isabstractmethod__(self):
return getattr(self.func, '__isabstractmethod__', False)
并将其应用于您的 Vector()
类。您仍然必须注册 Vector
类创建后的单个调度 的实现,因为只有这样才能注册该类的调度:
and apply that to your Vector()
class. You still have to register your Vector
implementation for the single dispatch after the class has been created, because only then can you register a dispatch for the class:
class Vector(object):
def __init__(self, d):
self._coords = [0] * d
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __repr__(self):
return f"Vector({self._coords!r})"
def __str__(self):
return str(self._coords)
@singledispatchmethod
def __mul__(self, other):
return NotImplemented
@__mul__.register
def _int_mul(self, other: int):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
@Vector.__mul__.register
def _vector_mul(self, other: Vector):
return sum(sc * oc for sc, oc in zip(self._coords, other._coords))
当然,您也可以创建子类f irst和调度基于此,因为调度也适用于子类:
You could of course also create a subclass first and dispatch based on that, since dispatch works for subclasses too:
class _Vector(object):
def __init__(self, d):
self._coords = [0] * d
class Vector(_Vector):
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __repr__(self):
return f"{type(self).__name__}({self._coords!r})"
def __str__(self):
return str(self._coords)
@singledispatchmethod
def __mul__(self, other):
return NotImplemented
@__mul__.register
def _int_mul(self, other: int):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
@__mul__.register
def _vector_mul(self, other: _Vector):
return sum(sc * oc for sc, oc in zip(self._coords, other._coords))
这篇关于python3:类中的singledispatch,如何调度自类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!