python3: singledispatch in class, how to dispatch self type
使用python3.4。在这里,我想使用singledispatch在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
正如你看到的代码,我想要支持向量*顶点,这有名称错误。
1 2 3 4 5 6 | 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 |
号
问题可能是如何在类的方法中使用类名A类型?我知道C++有字体类语句。python如何解决我的问题?奇怪的是,在方法体中可以使用
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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() |
ANS很奇怪:
1
2
3
4
5
6
7
8
9
10
11
12 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")
号
在@martijn pieters的评论之后更新,我仍然希望在类中实现v*3。所以我试试这个
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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() |
。
这次。我像执行
1
2
3
4
5
6 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
号
对于我来说,静态方法应该像实例方法一样。
您根本不能在方法上使用
这里还没有定义
因为修饰符在创建类对象之前应用于函数对象,所以您可以在类主体之外将"方法"注册为函数,这样您就可以访问
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 |
对于不支持的类型,您需要返回
但是,由于调度将在这里输入错误的参数(
如果您真的想使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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 |
号
关于您使用
这里
换句话说,python不会使用您在
python 3.8添加的
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 31 32 33 34 35 36 37 | 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) |
并将其应用于您的
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 31 32 33 | 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)) |
。
当然,您也可以先创建一个子类,然后基于它进行分派,因为分派也适用于子类:
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 31 32 33 34 | 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)) |
这有点难看,因为您需要推迟绑定
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 31 | import functools class Vector: def __mul__(self, other): # Python has already dispatched Vector() * object() here, so # swap the arguments so that our single-dispatch works. Note # that in general if a*b != b*a, then the _mul_by_other # implementations need to compensate. return Vector._mul_by_other(other, self) @functools.singledispatch def _mul_by_other(x, y): raise NotImplementedError("Can't multiply vector by {}".format(type(x))) @_mul_by_other.register(int) def _(x, y): print("Multiply vector by int") @Vector._mul_by_other.register(Vector) def _(x, y): print("Multiply vector by another vector") x = Vector() y = Vector() x * 3 x * y try: x *"foo" except NotImplementedError: print("Caught attempt to multiply by string") |
。