بيثون3:إرسال فردي في الفصل، كيفية إرسال النوع الذاتي

StackOverflow https://stackoverflow.com//questions/24063788

سؤال

باستخدام بايثون3.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

قد يكون السؤال كيف يمكنني استخدام اسم الفئة "أ" في طريقة الفصل؟أعلم أن 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 مفرد, ، لا تثير استثناء.بهذه الطريقة ستحاول بايثون إجراء العملية العكسية أيضًا.

ومع ذلك، نظرًا لأن الإرسال سيتم تشغيله حجة خاطئة (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), ، يرى بحث عن طريقة خاصة في مرجع نموذج بيانات بايثون.هذا دائماً يتجاوز أي أساليب تم تعيينها مباشرة على المثيل.

هنا type(v) يكون Vector;تبحث بايثون عن وظيفة, ، لن يستخدم طريقة ملزمة هنا.مرة أخرى، لأن functools.singledispatch الإرساليات على أولاً الوسيطة، دائمًا، لا يمكنك استخدام إرسال واحد مباشرة على الأساليب Vector, ، لأن هذه الوسيطة الأولى ستكون دائمًا a 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")
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top