質問

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*3v.__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")
.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top