В Python существует ли какая-либо разница (семантика, эффективность и т.д.) между написанием x = x+1
и x += 1
?
Python Разница между x = x + 1 и x + = 1
Ответ 1
Да. В зависимости от того, как кодируется класс x
, короткая форма имеет возможность изменять x на месте вместо того, чтобы создавать новый объект, представляющий сумму, и возвращать его обратно к тому же имени. Это имеет значение, если у вас есть несколько переменных, все ссылающиеся на один и тот же объект - например, со списками:
>>> a = b = []
>>> a += [5]
>>> a
[5]
>>> b
[5]
>>> a = a + [5]
>>> a
[5, 5]
>>> b
[5]
Это происходит потому, что за кулисами операторы называют разные магические методы: +
вызывает __add__
или __radd__
(которые, как ожидается, не изменят ни один из их аргументов), и +=
try __iadd__
(который разрешено изменять self
, если это похоже на него), прежде чем возвращаться к логике +
, если __iadd__
не существует.
Ответ 2
Они почти одинаковы для целых чисел и float, но для lists
:
lis = lis+['foo']
создает новый список путем объединения lis
и ['foo']
, а затем присваивает результат lis
и:
lis += [foo]
эквивалентен lis.extend([foo])
>>> lis = [1,2,3]
>>> id(lis)
3078880140L
>>> lis += ['foo'] #or lis.extend(['foo'])
>>> id(lis) #same object
3078880140L
>>> lis = [1,2,3]
>>> id(lis)
3078880076L
>>> lis = lis+['foo']
>>> id(lis) #new object
3078880012L
Ответ 3
Да, это разные операторы, которые компилируются в разные байт-коды:
>>> import dis
>>> def test1(x):
... x = x + 1
...
>>> def test2(x):
... x += 1
...
>>> dis.dis(test1)
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (1)
6 BINARY_ADD
7 STORE_FAST 0 (x)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
>>> dis.dis(test2)
2 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (1)
6 INPLACE_ADD
7 STORE_FAST 0 (x)
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
В этом случае это не будет иметь большого значения, поскольку int
являются неизменяемыми. Теоретически они могут быть реализованы по-разному (в зависимости от интерпретатора), но это не изменит способ работы с этим значением.
В общем, они могут быть реализованы для выполнения совершенно разных вещей (+
реализуется магическим методом __add__()
и +=
с помощью __iadd()__
)), например, в большинстве изменяемых контейнеров он делает огромную разница, если у вас есть разные имена, ссылающиеся на один и тот же объект:
>>> x = []
>>> y = x
>>> x += [1]
>>> y
[1]
>>> x = x + [1]
>>> y
[1]
>>> x
[1, 1]
Вы можете видеть, что когда мы назначаем x
на y
, они оба указывают на один и тот же список. Когда мы используем +=
, мы расширяем список и оба изменяем. Когда мы присваиваем новое значение x
, y
все еще указывает на оригинал и остается неизменным.
Ответ 4
Они разные, потому что для +
и +=
существуют отдельные операторы. С x = x + 1
интерпретатор будет относиться к нему как x = x.__add__(1)
, а x += 1
будет x = x.__iadd(1)
, что может быть намного более эффективным, поскольку необязательно делать копию x
.