2009-06-26 9 views
38

¿Es posible anular + = en Python?Anulando "+ =" en Python? (Método __iadd __())

+1

Duplicado exacto de http://stackoverflow.com/questions/728361/is-there-a-way-to-overload-in-python –

+4

sí, pero el uso de la búsqueda del sitio para: + = python no arroja resultados. poner comillas alrededor de "+ =" da resultados, pero en su mayoría irrelevantes a juzgar por los titulares. encontró esto a través de una búsqueda en Google sin embargo. –

Respuesta

76

Sí, anule el método __iadd__. Ejemplo:

def __iadd__(self, other): 
    self.number += other.number 
    return self  
+30

No debe implementar '__iadd__' si su clase representa objetos inmutables. En ese caso, simplemente implemente '__add__' que se usará para anular' + = 'en su lugar. Por ejemplo, puede usar '+ =' en tipos inmutables como cadenas y enteros, lo que no podría hacerse usando '__iadd__'. –

+0

@ScottGriffiths, ¿estás diciendo que si has implementado '__add__' no necesariamente tienes que implementar' __iadd__'? Leí la pregunta duplicada que me vinculó, pero me confundí porque no entiendo por qué implementaría '__add__' para que mutara el objeto –

+0

@ScottGriffiths quería decir" no necesariamente tiene que implementar '__iadd__'__ para usar + = __? " –

12

Además de la sobrecarga __iadd__ (recuerde que debe volver auto!), También se puede repliegue en __add__, cuando x = y + funcionará como x = x + y. (Esta es una de las trampas del operador + =.)

>>> class A(object): 
... def __init__(self, x): 
...  self.x = x 
... def __add__(self, other): 
...  return A(self.x + other.x) 
>>> a = A(42) 
>>> b = A(3) 
>>> print a.x, b.x 
42 3 
>>> old_id = id(a) 
>>> a += b 
>>> print a.x 
45 
>>> print old_id == id(a) 
False 

Incluso trips up experts:

class Resource(object): 
    class_counter = 0 
    def __init__(self): 
    self.id = self.class_counter 
    self.class_counter += 1 

x = Resource() 
y = Resource() 

¿Qué valores esperas x.id, y.id y Resource.class_counter que tienen?

+7

Su segundo ejemplo no tiene nada que ver con iadd o + =. El mismo resultado se produce si usa self.class_counter = auto.class_counter + 1 Es solo un problema de alcance, usando self cuando se debe usar Resource. – FogleBird

+0

Es un ejemplo de cómo usar + = puede ocasionar problemas. Si está sobrecargando __iadd__, está abriendo a los usuarios de su clase (incluido usted) a esto y, como mínimo, debe saber que el problema existe de antemano. –

+2

@FogleBird: es un gotcha porque 'foo + = bar' puede significar" mutar el objeto existente que 'foo' se refiere a" o "asignar' foo' al objeto resultante de la expresión 'foo + bar'". Y lo que sucede depende de si 'foo' tiene un método' __iadd__'. – Claudiu

9

Además de lo que se da correctamente en las respuestas anteriores, vale la pena aclarar explícitamente que cuando se anula __iadd__, la operación x += y NO finaliza con el final del método __iadd__.

En su lugar, termina con x = x.__iadd__(y). En otras palabras, Python asigna el valor de retorno de su implementación __iadd__ al objeto que está "agregando", DESPUÉS de que se complete la implementación.

Esto significa que es posible mutar el lado izquierdo de la operación x += y para que falle el último paso implícito. Considere lo que puede ocurrir cuando se agrega a algo que está dentro de una lista:

>>> x[1] += y # x has two items

Ahora, si su __iadd__ aplicación (un método de un objeto en x[1]) erróneamente oa propósito elimina el primer elemento (x[0]) desde el principio de la lista, Python ejecutará su método __iadd__) & intente asignar su valor de retorno a x[1]. Que ya no existirá (estará en x[0]), lo que da como resultado un ÌndexError.

O, si sus __iadd__ insertos algo a principios de x del ejemplo anterior, el objeto será en x[2], no x[1], y lo que era antes en x[0] ahora estará en x[1] y asignar el valor de retorno de la __iadd__ invocación.

A menos que uno entienda lo que está sucediendo, los errores resultantes pueden ser una pesadilla para solucionar.