В чем разница между x+=y и x=x+y?

1,00
р.
Есть ли разница между
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