In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
Как эта замена значений a и b работает внутри? Он определенно не использует временную переменную.
In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
Как эта замена значений a и b работает внутри? Он определенно не использует временную переменную.
Python отделяет правое выражение от назначения левой стороны. Сначала оценивается правая часть, и результат сохраняется в стеке, а затем имена левой стороны назначаются с использованием кодов операций, которые снова принимают значения из стека.
Для назначений кортежей с 2 или 3 элементами Python просто использует стек напрямую:
>>> import dis
>>> def foo(a, b):
... a, b = b, a
...
>>> dis.dis(foo)
2 0 LOAD_FAST 1 (b)
3 LOAD_FAST 0 (a)
6 ROT_TWO
7 STORE_FAST 0 (a)
10 STORE_FAST 1 (b)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
После двух LOAD_FAST opcodes (которые выталкивают значение из переменной в стек) верхняя часть стека содержит [a, b], Оператор ROT_TWO заменяет верхние две позиции в стеке, поэтому стек теперь имеет [b, a] вверху. Два STORE_FAST opcodes затем берут эти два значения и сохраняют их в именах в левой части задания. Первая STORE_FAST выводит значение вершины стека и помещает ее в a, затем снова появляется сообщение, сохраняя значение в b. Вращение необходимо, потому что Python гарантирует, что назначения в целевом списке с левой стороны выполняются слева направо.
Для назначения трех имен ROT_THREE, за которым следует ROT_TWO, выполняется обращение к трем верхним элементам в стеке.
Для более длинных левосторонних назначений строит явный кортеж:
>>> def bar(a, b, c, d):
... d, c, b, a = a, b, c, d
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 LOAD_FAST 2 (c)
9 LOAD_FAST 3 (d)
12 BUILD_TUPLE 4
15 UNPACK_SEQUENCE 4
18 STORE_FAST 3 (d)
21 STORE_FAST 2 (c)
24 STORE_FAST 1 (b)
27 STORE_FAST 0 (a)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
Здесь стек с [d, c, b, a] используется для построения кортежа (в обратном порядке BUILD_TUPLE снова появляется из стека, нажав результирующий кортеж в стек), а затем UNPACK_SEQUENCE снова выставляет кортеж из стека, отбрасывает все элементы из кортежа обратно на стека снова для операций STORE_FAST.
Последний может показаться расточительной операцией, но правая часть задания может быть чем-то совершенно другим, вызов функции, который создает кортеж, возможно, поэтому интерпретатор Python не делает предположений и использует код операции UNPACK_SEQUENCE всегда. Он делает это даже для операций присваивания двух и трех имен, но шаг оптимизации (глазок) заменяет BUILD_TUPLE/UNPACK_SEQUENCE сочетание с 2 или 3 аргументами с приведенными выше кодами ROT_TWO и ROT_THREE для эффективности.