python3:类中的singledispatch,如何调度自类型 [英] python3: singledispatch in class, how to dispatch self type

查看:186
本文介绍了python3:类中的singledispatch,如何调度自类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用python3.4。在这里,我想使用singledispatch在 __ mul __ 方法中调度不同的类型。像这样的代码:

Using python3.4. Here I want use singledispatch to dispatch different type in __mul__ method . The code like this :

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 ,这有名称错误

As you can see the code , I want support Vector*Vertor , This has Name error

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

问题可能是我该如何使用类名类型在类的方法中?我知道c ++有font类声明。 python如何解决我的问题?奇怪的是看到 result = Vector(len(self))其中 Vector 可以在方法主体中使用

The question may be How can I use class name a Type in the class's method ? I know c++ have font class statement . How python solve my problem ? And it is strange to see result = Vector(len(self)) where the Vector can be used in method body .

之后,请看 http://lukasz.langa.pl/8/single-dispatch-generic-functions/
我可以选择这种方式来实现:

After have A look at http://lukasz.langa.pl/8/single-dispatch-generic-functions/ I can choose this way to implement :

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)相同。

v.__mul__(3) can work but v*3 can't work. This is strange From my option v*3 is just the same as v.__mul__(3) .

在@Martijn Pieters评论后更新,我仍然希望实现 v * 3 在班上。所以我试试这个

Update after @Martijn Pieters's comment, I still want implement v*3 in class. So I try this

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


对我而言,静态方法应像实例方法一样工作。

For me static method should act like the instance method.

推荐答案

您不能在方法上使用 functools.singledispatch 全部,至少不作为装饰者。 Python 3.8仅针对方法添加了一个新选项: functools.singledispatchmethod()

You cannot use functools.singledispatch on methods at all, not as a decorator at least. Python 3.8 adds a new option, just for methods: functools.singledispatchmethod().

Vector 尚未在此处定义;任何方法的第一个参数始终将是 self ,而您在此处将第二个参数使用单调度。

It doesn't matter that Vector isn't defined here yet; the first argument to any method is always going to be self, while you'd use single dispatch for the second argument here.

由于装饰器在创建类对象之前应用于函数对象,因此您也可以将方法注册为函数,而不是 ,因此您可以访问 Vector 名称:

Because decorators apply to the function objects before the class object is created, you could just as well register your 'methods' as functions instead, outside of the class body, so you have access to the Vector name:

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也会尝试逆运算。

For non-supported types, you need to return the NotImplemented singleton, not raise an exception. This way Python will try the inverse operation too.

但是,由于分派将要键入错误的参数 self ),您将必须提出自己的单一调度机制。

However, since the dispatch is going to key on the wrong argument (self) here anyway, you'll have to come up with your own single dispatch mechanism.

如果您真的想使用 @ functools.singledispatch 您必须将其委托给常规函数,并使用参数 inversed

If you really want to use @functools.singledispatch you'd have to delegate to a regular function, with the arguments inversed:

@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 not 转换为 v .__ mul __(3)。而是将其翻译为 type(v).__ mul __(v,3),请参见 特殊方法查找 始终绕过直接在实例上设置的所有方法。

As for your updates using __init__mul__: v * 3 is not translated to v.__mul__(3). It is instead translated to type(v).__mul__(v, 3), see Special method lookup in the Python datamodel reference. This always bypasses any methods set directly on the instance.

此处 type(v) Vector ; Python查找 function ,此处将不使用绑定方法。同样,由于 first 参数上的 functools.singledispatch 分派,因此,始终不能直接在的方法上使用单分派Vector ,因为第一个参数始终是 Vector 实例。

Here type(v) is Vector; Python looks up the function, it won't use a bound method here. Again, because functools.singledispatch dispatches on the first argument, always, you cannot use single dispatch directly on the methods of Vector, because that first argument is always going to be a Vector instance.

换句话说,Python将使用您在 __ init__mul __ 中的 self 上设置的方法;特殊方法是从不在实例上查找,请参阅 特殊方法查找 在数据模型文档中。

In other words, Python will not use the methods you set on self in __init__mul__; special methods are never looked up on the instance, see Special method lookup in the datamodel documentation.

functools.singledispatchmethod()选项使用 class 作为装饰器,该装饰器实现了描述符协议,就像方法一样。这样,它便可以处理在 绑定之前的分派(因此,在 self 之前要放在参数列表之前),然后绑定 singledispatch 调度程序返回。 此实现的源代码是完全兼容的使用较旧的Python版本,因此您可以改用它:

The functools.singledispatchmethod() option that Python 3.8 adds uses a class as the decorator which implements the descriptor protocol, just like methods do. This lets it then handle dispatch before binding (so before self would be prepended to the argument list) and then bind the registered function that the singledispatch dispatcher returns. The source code for this implementation is fully compatible with older Python versions, so you could use that instead:

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 类创建后的单个调度 的实现,因为只有这样才能注册该类的调度:

and apply that to your Vector() class. You still have to register your Vector implementation for the single dispatch after the class has been created, because only then can you register a dispatch for the class:

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))

当然,您也可以创建子类f irst和调度基于此,因为调度也适用于子类:

You could of course also create a subclass first and dispatch based on that, since dispatch works for subclasses too:

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))

这篇关于python3:类中的singledispatch,如何调度自类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆