python3:singledispatch en clase, cómo envío auto tipo
-
02-01-2020 - |
Pregunta
El uso de python3.4.Aquí quiero que el uso de singledispatch para el envío de otro tipo en __mul__
método .El código como este :
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
Como se puede ver el código , quiero apoyo Vector*Vertor
Este tiene el Nombre de 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
La pregunta puede ser ¿Cómo puedo utilizar el nombre de clase de un Tipo en la clase del método ?Sé c++ fuente de la clase de instrucción .Cómo python solucionar mi problema ?Y es extraño ver result = Vector(len(self))
donde el Vector
puede ser utilizado en el cuerpo del método .
Después de echar Un vistazo a http://lukasz.langa.pl/8/single-dispatch-generic-functions/ Puedo elegir esta forma de implementar :
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()
La respuesta es extraño :
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)
puede funcionar, pero v*3
no puede trabajar.Esto es extraño Desde mi opción v*3
es el mismo que v.__mul__(3)
.
Actualización después de @Martijn Pieters comentario, todavía quiero implementar v*3
en la clase.Así que trate de este
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()
Esta vez . v.__mul__(3)
han de error :
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
Para mí el método estático debe actuar como el método de instancia.
Solución
Usted puede utilizar functools.singledispatch
en los métodos de en todos los, no como un decorador, al menos.Python 3.8 añade una nueva opción, sólo por métodos: functools.singledispatchmethod()
.
No importa que Vector
no está definido aún aquí;el primer argumento a cualquier método siempre va a ser self
, mientras que usted usaría solo envío para el segundo argumento aquí.
Debido a que los decoradores se aplican a la la función de los objetos antes de la clase de objeto es creado, usted puede registrar su 'métodos' como funciones en su lugar, fuera de del cuerpo de la clase, por lo que tiene acceso a la Vector
nombre:
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
Para que no se admiten los tipos, necesita devolver el NotImplemented
singleton, y no elevar una excepción.De esta manera Python va a tratar de la operación inversa también.
Sin embargo, desde que el envío se va a clave en la mal argumento (self
) aquí de todos modos, tendrás que venir para arriba con su propia único envío mecanismo.
Si usted realmente desea utilizar @functools.singledispatch
usted tendría que delegar una función regular, con los argumentos invertida:
@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
Como para sus actualizaciones a través de la __init__mul__
: v * 3
es no traducido a v.__mul__(3)
.Es lugar traducido a type(v).__mul__(v, 3)
, ver Método especial de búsqueda en Python datamodel de referencia.Este siempre omite alguno de los métodos establecidos directamente en la instancia.
Aquí type(v)
es Vector
;Python busca la la función, no utilizar un método enlazado aquí.De nuevo, porque functools.singledispatch
informes sobre la primero argumento, siempre, no se puede utilizar solo envío directamente en los métodos de Vector
, debido a que el primer argumento es siempre va a ser un Vector
ejemplo.
En otras palabras, Python no el uso de los métodos establecidos en los self
en __init__mul__
;métodos especiales son nunca miró en la instancia, ver Método especial de búsqueda en el datamodel documentación.
El functools.singledispatchmethod()
la opción de que Python 3.8 añade utiliza un clase como el decorador que implementa el descriptor de protocolo, como métodos de hacer.De este modo, luego de la manija de envío antes de unión (así que antes de self
sería añadida a la lista de argumentos) y, a continuación, enlazar la función registrado que el singledispatch
despachador devuelve.El código fuente de esta aplicación es totalmente compatible con las anteriores versiones de Python, por lo que podría utilizar en su lugar:
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)
y aplicar eso a su Vector()
clase.Usted todavía tiene que registrar su Vector
aplicación para el solo envío después de la clase ha sido creado, porque sólo entonces se puede registrar un despacho para la clase:
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))
Usted podría, por supuesto, también crear una subclase de primer y el envío basado en que, desde el envío de obras para subclases también:
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))
Otros consejos
Esto es un poco feo, como es necesario diferir la unión de la implementación de Vector
/Vector
multiplicación hasta después de Vector
es realmente definido.Pero la idea es que la única función de envío de las necesidades de la primer argumento de tipo arbitrario, por lo que Vector.__mul__
le llame a esa función con self
como segundo argumento.
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")