Python:クラスのシングルスパッチ、自己タイプを発送する方法
-
02-01-2020 - |
質問
Python3.4を使用しています。ここでは、cingledispatchを使用して__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
.
クラスのメソッドでクラス名Aタイプを使用する方法かもしれません。 C ++がFont Classステートメントを持っていることを知っています。 Pythonが私の問題を解決するの? result = Vector(len(self))
がメソッド本体で使用できるVector
を見るのは奇妙です。
http://lukasz.langa.pl / 8 /単一ディスパッチ一般機能/ この方法を実装するには、次のように選択できます。
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
になりますが、 2番目の引数はここでは単一のディスパッチを使用します。
クラスオブジェクトが作成される前にデコレータが関数オブジェクトに適用されるので、クラス本体の関数としての「メソッド」を関数として登録することができます、 それで、あなたは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
Singleton を返す必要があります。このようにしてPythonも逆操作を試みます。
しかし、Dispatchは間違った引数(self
)でキーを押すと、あなた自身の単一のディスパッチメカニズムを思いつく必要があります。
本実験的な関数に委任する必要がある場合は、引数逆:
を使用して、通常の関数に委任する必要があります。@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
.
@functools.singledispatch
:__init__mul__
を使用しているのは、v * 3
に変換されていません。それは代わりにv.__mul__(3)
に翻訳されます。 特殊メソッドLookup Python DataModelリファレンスの 。この常時は、インスタンス上に直接設定されたメソッドをバイパスします。
ここtype(v).__mul__(v, 3)
はtype(v)
です。 Pythonは関数を調べて、ここではバインドメソッドを使用しません。繰り返しますが、常に first 引数でディスパッチしているため、常にVector
のメソッド上に直接シングルディスパッチを使用することはできません。
つまり、Pythonは NOT がfunctools.singledispatch
で設定したメソッドを使用します。特別なメソッドはインスタンス上で検索されていませんでした。 "> 特殊メソッドLookup はDataModelのドキュメントです。
Python 3.8が追加されたVector
オプションは、 class を使用します。="nofollow noreferrer">ディスクリプタプロトコルは、どおりと同じようにします。これにより、Dispatch バインディング(Vector
が引数リストに追加される前に)を処理してから、self
ディスパッチャが返す登録機能をバインドします。 この実装のソースコードは完全に古い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)
.
を__init__mul__
クラスに適用します。クラスのディスパッチを登録できるのは、クラスのディスパッチを登録できます。
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__
はその関数を2番目の引数として呼び出します。
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")
.