بيثون3:إرسال فردي في الفصل، كيفية إرسال النوع الذاتي
-
02-01-2020 - |
سؤال
باستخدام بايثون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")