python3:在课堂上的singledispatch,如何调度自我类型

jia*_*amo 10 python types class dispatch python-3.4

使用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 
Run Code Online (Sandbox Code Playgroud)

正如你可以看到代码,我想要支持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
Run Code Online (Sandbox Code Playgroud)

问题可能是如何在类的方法中使用类名称类型?我知道c ++有字体类声明.python如何解决我的问题?看到方法体中可以使用的Vector*Vertor位置很奇怪result = Vector(len(self)).更新.看看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()
Run Code Online (Sandbox Code Playgroud)

ans很奇怪:

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")
Run Code Online (Sandbox Code Playgroud)

Vector 可以工作,但v.__mul__(3)不能工作.这很奇怪从我的选择v*3是一样的v*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() 
Run Code Online (Sandbox Code Playgroud)

这次 .我的实现就像我实现的那样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
Run Code Online (Sandbox Code Playgroud)

对我来说,静态methond应该像实例methond一样.

Mar*_*ers 15

您不能使用functools.singledispatch的方法在所有的,而不是至少一个装饰.

functools.singledispatchmethod()这里没有定义并不重要; 任何方法的第一个参数总是如此Vector,而你在这里使用单个调度作为第二个参数.

因为装饰器在创建类对象之前应用于函数对象,所以您也可以将"方法"作为函数注册,而不是在类体外部,因此您可以访问该self名称:

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
Run Code Online (Sandbox Code Playgroud)

对于不受支持的类型,您需要返回Vector 单例,而不是引发异常.这样Python也会尝试逆操作.

但是,由于调度将在此处键入错误的参数(NotImplemented),因此您必须提出自己的单一调度机制.

如果你真的想要使用,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
Run Code Online (Sandbox Code Playgroud)

至于使用更新@functools.singledispatch:__init__mul__不是翻译成v * 3.它被翻译为v.__mul__(3),请参阅Python数据模型参考中的特殊方法查找.这总是绕过直接在实例上设置的任何方法.

type(v).__mul__(v, 3)type(v); Python查找函数,它不会在这里使用绑定方法.同样,因为总是Vector在第一个参数上调度,所以不能直接在方法上使用单个调度functools.singledispatch,因为第一个参数始终是一个Vector实例.

换句话说,Python将不会使用你设置的方法Vectorself; 特殊的方法从来没有抬头的情况下,看到特殊的方法查找数据模型文档.