При множественном присваивании Python сначала вычисляет правую часть целиком или записывает значения по мере разбора левых целей?
Python сначала вычисляет правую часть, а затем присваивает полученные значения левым целям слева направо. Поэтому обмен значений работает без временной переменной, но перекрывающиеся цели могут дать результат, зависящий от порядка присваивания.
Множественное присваивание объединяет распаковку значений и обычное присваивание в одной конструкции. Оно позволяет компактно передавать несколько результатов и выражать обмен значений без явной временной переменной.
При этом это не означает настоящего одновременного изменения всех объектов. Python разделяет вычисление правой части и последующую запись в цели.
Если левая и правая части используют одни и те же имена, важно понимать границу между чтением старых значений и записью новых. Ошибка в этом понимании особенно опасна при присваивании атрибутам, элементам списков или словарей.
Нельзя считать, что все левые цели получают значения одновременно. После вычисления правой части цели обрабатываются последовательно, поэтому запись в первую цель может повлиять на вычисление следующей цели.
Сначала Python вычисляет все выражения правой части в порядке слева направо. Если требуется распаковка, полученные значения или итерируемый объект должны соответствовать числу целевых элементов с учётом звёздочной цели.
Затем Python присваивает значения целям слева направо. Имена, атрибуты и элементы контейнеров обрабатываются именно на этом этапе, поэтому выражения внутри целей могут увидеть уже изменённое состояние.
Сначала правая часть получает значения 1 и 99. Затем index становится равен 1, и вторая цель обращается уже к values[1], поэтому результатом будет список [10, 99].
При обычном обмене значения правой части сохраняются до начала записи:
Здесь обе правые ссылки вычисляются до изменения a и b, поэтому результат — 2 1.
Фраза «правая часть вычисляется целиком» не означает, что Python заранее материализует любой объект. Например, вызов, возвращающий генератор, создаёт генератор; его тело начнёт выполняться только при итерации или распаковке. Важен порядок вычисления выражений и целей, а не обязательное создание кортежа во всех случаях.
В обработчике данных разработчик хотел заменить два поля объекта одновременно и использовал множественное присваивание. В правой части одно значение вычислялось из старого состояния, а в левой части присутствовали атрибут и элемент списка, причём вторая цель зависела от значения, записанного первой.
Рассматривались два варианта. Множественное присваивание короче и безопасно для независимых целей, но плохо читается при перекрывающихся целях. Последовательные присваивания понятнее в сложном случае, однако требуют явно сохранить старое значение, если оно нужно нескольким операциям.
Выбрали последовательные присваивания с временной переменной. Это устранило зависимость от порядка целей и сделало намерение очевидным для ревьюеров. Множественное присваивание оставили только там, где цели независимы или используется стандартный обмен значений.
Нет. Вычисление правой части происходит до записи, но сами присваивания целям выполняются последовательно. Если запись в одну цель вызовет исключение, предыдущие записи могут уже остаться выполненными, поэтому множественное присваивание не является транзакцией и не откатывается автоматически.
Python распакует итерируемый объект и проверит соответствие количества элементов целям. При нехватке или избытке элементов возникнет ValueError; звёздочная цель может принять произвольное количество оставшихся элементов и получить список.
Важно различать вычисление объекта и его перебор: сам объект справа вычисляется сначала, но при распаковке его итератор может выполнять пользовательский код и выбрасывать исключение.
Потому что правая часть фиксирует значения заранее, а левые цели изменяются по очереди. Выражения в последующих целях, например индекс или объект-контейнер, вычисляются уже после предыдущих записей.
Поэтому в простом обмене имён множественное присваивание надёжно, а в конструкциях с индексами, атрибутами и побочными эффектами лучше использовать промежуточные переменные или отдельные инструкции. Это снижает зависимость результата от порядка обработки целей.