В чем разница между x+=y и x=x+y?
Есть ли разница между
x += y
и
x = x + y
для встроенных типов? Если не в семантике, то в быстродействии?
Есть ли разница между
x += y
и
x = x + y
для встроенных типов? Если не в семантике, то в быстродействии?
NB Это всё проверялось для CPython 3.10. В других интерпретаторах/компиляторах результат может быть другим (другим по времени, но тем же по результату!).
Разница в одной инструкции: INPLACE_ADD
или BINARY_ADD
.
def f(): def g(): x = 10 x = 10 x += 1 x = x + 1 0 LOAD_CONST 1 (10) 0 LOAD_CONST 1 (10) 2 STORE_FAST 0 (x) 2 STORE_FAST 0 (x) 4 LOAD_FAST 0 (x) 4 LOAD_FAST 0 (x) 6 LOAD_CONST 2 (1) 6 LOAD_CONST 2 (1) 8 INPLACE_ADD 8 BINARY_ADD 10 STORE_FAST 0 (x) 10 STORE_FAST 0 (x) 12 LOAD_CONST 0 (None) 12 LOAD_CONST 0 (None) 14 RETURN_VALUE 14 RETURN_VALUE
Для целых и вещественных чисел слот nb_inplace_add
не заполнен (например для целых). Это значит что обе инструкции INPLACE_ADD
и BINARY_ADD
выполняют один и тот же код из слота nb_add
. Результат работы и время одинаковы и в теории и на практике.
Для строк и кортежей ситуация такая же. Инструкции исполняют один и тот же код - результат одинаковый и время одинаковое.
Тут хитрее: отличается и результат и время. +=
выигрывает, так как не изготавливает новый список - конкатенацию аргументов, а сразу добавляет элементы правого списка в левый. Примеры ниже немного нечестные по отношению к +=
- список n
растёт. Тем не менее +=
лидирует с огромным отрывом.
NB Разная семантика! f
и g
по разному работают со списком n
. f
его меняет, g
оставляет неизменным.
В случае списков последняя инструкция STORE_FAST
не нужна - это фактически самоприсваивание. Но код не знает про типы и делает лишнюю операцию. Мелочь, показывающая почему компиляторы любят строгую типизацию.
Очень быстро:
n = [0] * 1000
m = [1]
def f():
x = n
x += m
for _ in range(1_000_000):
f()
$ time python f.py real 0m0.202s user 0m0.200s sys 0m0.000s
Очень медленно:
n = [0] * 1000
m = [1]
def g():
x = n
x = x + m
for _ in range(1_000_000):
g()
$ time python g.py real 0m4.403s user 0m4.296s sys 0m0.004s
можно заметить разницу при работе со списками:
x = [1, 2, 3]
y = [4, 5, 6]
z = x
x = x + y
x[0] = 10
print(z)
и
x = [1, 2, 3]
y = [4, 5, 6]
z = x
x += y
x[0] = 10
print(z)
вывод будет разный. Это происходит потому что первый вариант создаёт новый список, а второй вариант добавляет элементы в конец.
когда происходит сложение то вызывается __add__
, который возвращает новый объект. А когда +=
вызывается __iadd__
, который меняет текущий объект. Если же __iadd__
отсутствует то результат будет абсолютно таким же как при обычном сложение.