symbol - Quando é "i+= x" diferente de "i=i+x" em Python?
python operator airflow (3)
Aqui está um exemplo que compara diretamente i += x
com i = i + x
:
def foo(x):
x = x + [42]
def bar(x):
x += [42]
c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]
https://code.i-harness.com
Foi-me dito que +=
pode ter efeitos diferentes da notação padrão de i = i +
. Existe um caso em que i += 1
seria diferente de i = i + 1
?
Isso depende inteiramente do objeto i
.
+=
chama o método __iadd__
(se existir - voltando a __add__
se ele não existir) enquanto que +
chama o método __add__
1 ou o método __radd__
em alguns casos 2 .
De uma perspectiva API, __iadd__
deve ser usado para modificar objetos mutáveis no lugar (retornando o objeto que foi mutado) enquanto que __add__
deve retornar uma nova instância de algo. Para objetos imutáveis , os dois métodos retornam uma nova instância, mas __iadd__
colocará a nova instância no namespace atual com o mesmo nome da instância antiga. Isso é por que
i = 1
i += 1
parece incrementar i
. Na realidade, você obtém um novo inteiro e o atribui "em cima de" i
- perdendo uma referência ao inteiro antigo. Nesse caso, i += 1
é exatamente o mesmo que i = i + 1
. Mas, com a maioria dos objetos mutáveis, é uma história diferente:
Como um exemplo concreto:
a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a #[1, 2, 3, 1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
comparado com:
a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
observe como no primeiro exemplo, desde b
e a
referência ao mesmo objeto, quando eu uso +=
on b
, ele realmente muda b
(e também vê essa mudança - Afinal, ele está referenciando a mesma lista). No segundo caso, no entanto, quando eu faço b = b + [1, 2, 3]
, isso leva a lista que b
está referenciando e concatena com uma nova lista [1, 2, 3]
. Em seguida, ele armazena a lista concatenada no namespace atual como b
- Sem considerar o que b
era a linha anterior.
1 Na expressão x + y
, se x.__add__
não é implementado ou se x.__add__(y)
retorna NotImplemented
e x
e y
possuem tipos diferentes , então x + y
tenta chamar y.__radd__(x)
. Então, no caso de você ter
foo_instance += bar_instance
Se Foo
não implementar __add__
ou __iadd__
então o resultado aqui é o mesmo que
foo_instance = bar_instance.__radd__(bar_instance, foo_instance)
2 Na expressão foo_instance + bar_instance
, bar_instance.__radd__
será tentado antes de foo_instance.__add__
se o tipo de bar_instance
for uma subclasse do tipo foo_instance
(por exemplo, issubclass(Bar, Foo)
). O racional para isso é porque Bar
é, em certo sentido, um objeto de "nível mais alto" do que Foo
então Bar
deveria ter a opção de Foo
o comportamento de Foo
.
Se você está lidando apenas com literais, então i += 1
tem o mesmo comportamento que i = i + 1
.