relación operadores operador matemáticos ejercicios doble diferente con comando cadenas python class operator-overloading operators

operadores - python operador diferente de



Sobrecarga del operador en Python: manejo de diferentes tipos y orden de parámetros (2)

Esta pregunta ya tiene una respuesta aquí:

Tengo una clase simple que ayuda con operaciones matemáticas en vectores (es decir, listas de números). Mi Vector se puede multiplicar por otras instancias de Vector o un escalar ( float o int ).

En otros lenguajes más fuertemente tipados, crearía un método para multiplicar dos vector y un método separado para multiplicar un vector por y int / float . Todavía soy bastante nuevo en Python y no estoy seguro de cómo implementarlo. La única forma en que puedo pensar en hacerlo es anular __mul__() y probar el parámetro entrante:

class Vector(object): ... def __mul__(self, rhs): if isinstance(rhs, Vector): ... if isinstance(rhs, int) or isinstance(rhs, float): ...

Incluso si lo hago de esa manera, me veré obligado a multiplicar un Vector por un escalar como este:

v = Vector([1,2,3]) result = v * 7

¿Qué sucede si quiero invertir el orden de los operandos en la multiplicación?

result = 7 * v

¿Cuál es la forma correcta de hacerlo en Python?


Hay métodos especiales para operaciones inversas :

  • __rmul__ por el reverso de __mul__
  • y __radd__ por __add__ ,
  • ...

Se NotImplemented cuando el operador del lado izquierdo devuelve NotImplemented para la operación normal (por lo que la operación 2 + vector_instance probará primero: (2).__add__(vector_instance) pero si esto devuelve NotImplemented entonces se vector_instance.__radd__(2) ).

Sin embargo, no utilizaría isinstance comprobaciones de isinstance en los métodos aritméticos especiales, que darán lugar a mucha repetición de código.

En realidad, podría crear un caso especial en __init__ e implementar una conversión de escalares a un Vector allí:

class Vector(object): def __init__(self, x, y=None, z=None): if y is None and z is None: if isinstance(x, Vector): self.x, self.y, self.z = x.x, x.y, x.z else: self.x, self.y, self.z = x, x, x elif y is None or z is None: raise ValueError(''Either x, y and z must be given or only x'') else: self.x, self.y, self.z = x, y, z def __mul__(self, other): other = Vector(other) return Vector(self.x*other.x, self.y*other.y, self.z*other.z) __rmul__ = __mul__ # commutative operation def __sub__(self, other): other = Vector(other) return Vector(self.x-other.x, self.y-other.y, self.z-other.z) def __rsub__(self, other): # not commutative operation other = Vector(other) return other - self def __repr__(self): return ''Vector({self.x}, {self.y}, {self.z})''.format(self=self)

Esto debería funcionar como se esperaba:

>>> 2 - Vector(1, 2, 3) Vector(1, 0, -1) >>> Vector(1, 2, 3) - 2 Vector(-1, 0, 1) >>> Vector(1, 2, 3) * 2 Vector(2, 4, 6) >>> 2 * Vector(1, 2, 3) Vector(2, 4, 6)

Tenga en cuenta que este fue un borrador rápido y sucio (que podría tener varios errores). Solo quería presentar la "idea general" de cómo podría resolverse sin una carcasa especial del tipo en cada operación aritmética.


También necesita implementar __rmul__ . Cuando falla la llamada inicial a int.__mul__(7, v) , Python intentará luego con el type(v).__rmul__(v, 7) .

def __rmul__(self, lhs): return self * lhs # Effectively, turn 7 * v into v * 7

Como señala Rawing, simplemente podría escribir __rmul__ = __mul__ para esta definición. __rmul__ existe para permitir la multiplicación no conmutativa en la que simplemente diferir a __mul__ con los operandos invertidos no es suficiente.

Por ejemplo, si estaba escribiendo una clase de Matrix y deseaba apoyar la multiplicación por una lista anidada, por ejemplo,

m = Matrix(...) # Some 2 x 2 matrix n = [[1, 2], [3,4]] p = n * m

Aquí, la clase de list no sabría cómo hacer una lista múltiple por una instancia de Matrix , así que cuando la list.__mul__(n, m) falla, Python probaría Matrix.__rmul__(m, n) . Sin embargo, n * m y m * n son dos resultados diferentes en general, entonces Matrix.__rmul__(m, n) != Matrix.__mul__(m, n) ; __rmul__ tiene que hacer un poco de trabajo extra para generar la respuesta correcta.