파이썬3:클래스의 단일 디스패치, 자기 유형을 디스패치하는 방법
-
02-01-2020 - |
문제
Python3.4를 사용합니다.여기서는 Singledispatch를 사용하여 다른 유형을 전달하고 싶습니다. __mul__
방법 .코드는 다음과 같습니다.
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
, 이름 오류가 있습니다.
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
질문은 클래스 메소드에서 클래스 이름과 Type을 어떻게 사용할 수 있습니까?나는 C++에 글꼴 클래스 문이 있다는 것을 알고 있습니다.파이썬이 내 문제를 어떻게 해결합니까?그리고 보니 이상하네요 result = Vector(len(self))
어디에 Vector
메서드 본문에서 사용할 수 있습니다.
살펴 본 후 http://lukasz.langa.pl/8/single-dispatch-generic-functions/구현하려면 다음 방법을 선택할 수 있습니다.
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)
.
@Martijn Pieters의 의견 이후 업데이트되었지만 여전히 구현하고 싶습니다. v*3
클래스.그래서 나는 이것을 시도한다
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
나에게 있어 정적 메소드는 인스턴스 메소드처럼 작동해야 합니다.
해결책
당신은 사용할 수 없습니다 functools.singledispatch
방법에 조금도, 적어도 데코레이터로는 아닙니다.Python 3.8에는 메서드에 대해서만 새로운 옵션이 추가되었습니다. functools.singledispatchmethod()
.
그건 중요하지 않아 Vector
여기에는 아직 정의되지 않았습니다.모든 메소드에 대한 첫 번째 인수는 항상 self
, 단일 디스패치를 사용하는 동안 두 번째 인수 여기.
데코레이터가 다음에 적용되기 때문에 함수 객체 클래스 객체가 생성되기 전에 대신 '메서드'를 함수로 등록할 수도 있습니다. 밖의 클래스 본문에 액세스할 수 있습니다. Vector
이름:
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
하나씩 일어나는 것, 예외를 발생시키지 않습니다.이런 식으로 Python은 역연산도 시도합니다.
그러나 파견이 핵심이 될 것이기 때문에 잘못된 주장 (self
) 어쨌든 여기서는 자신만의 단일 디스패치 메커니즘을 생각해 내야 합니다.
정말로 사용하고 싶다면 @functools.singledispatch
인수를 사용하여 일반 함수에 위임해야 합니다. 반전:
@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
~이다 ~ 아니다 다음으로 번역됨 v.__mul__(3)
.대신 다음과 같이 번역됩니다. type(v).__mul__(v, 3)
, 보다 특수 메소드 조회 Python 데이터 모델 참조에서.이것 언제나 인스턴스에 직접 설정된 모든 메서드를 우회합니다.
여기 type(v)
~이다 Vector
;파이썬은 다음을 찾습니다. 기능, 여기서는 바인딩된 메서드를 사용하지 않습니다.또 왜냐하면 functools.singledispatch
에 파견 첫 번째 인수는 항상 단일 디스패치를 메서드에 직접 사용할 수 없습니다. Vector
, 그 첫 번째 인수는 항상 Vector
사례.
즉, 파이썬은 ~ 아니다 설정한 방법을 사용하세요. self
~에 __init__mul__
;특별한 방법은 절대 인스턴스를 찾아보았습니다. 특수 메소드 조회 데이터 모델 문서에서.
그만큼 functools.singledispatchmethod()
Python 3.8이 추가하는 옵션은 수업 구현하는 데코레이터로서 설명자 프로토콜, 메서드와 마찬가지로.그러면 발송을 처리할 수 있습니다. ~ 전에 바인딩(그래서 이전에 self
인수 목록 앞에 추가됨) 그런 다음 등록된 함수를 바인딩합니다. singledispatch
디스패처가 돌아옵니다.그만큼 이 구현의 소스 코드 이전 Python 버전과 완벽하게 호환되므로 대신 사용할 수 있습니다.
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
단일 디스패치를 위한 구현 ~ 후에 클래스가 생성된 것입니다. 그래야만 클래스에 대한 디스패치를 등록할 수 있기 때문입니다.
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))
물론 먼저 하위 클래스를 만들고 이를 기반으로 파견할 수도 있습니다. 파견은 하위 클래스에서도 작동하기 때문입니다.
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))
다른 팁
구현 바인딩을 연기해야 하기 때문에 이는 약간 보기 흉합니다. Vector
/Vector
이후까지의 곱셈 Vector
실제로 정의되어 있습니다.그러나 단일 디스패치 함수의 첫 번째 인수는 임의 유형이어야 한다는 아이디어가 있습니다. Vector.__mul__
해당 함수를 호출합니다. self
두 번째 인수로.
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")