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 .







operators