Как работают цепочки назначений?
Цитата из чего-то:
>>> x = y = somefunction()
совпадает с
>>> y = somefunction()
>>> x = y
Вопрос: есть ли
x = y = somefunction()
то же, что и
x = somefunction()
y = somefunction()
?
Исходя из моего понимания, они должны быть одинаковыми, потому что somefunction
может возвращать только одно значение.
Ответы
Ответ 1
Они не обязательно будут работать одинаково, если somefunction
возвращает изменяемое значение. Рассмотрим:
>>> def somefunction():
... return []
...
>>> x = y = somefunction()
>>> x.append(4)
>>> x
[4]
>>> y
[4]
>>> x = somefunction(); y = somefunction()
>>> x.append(3)
>>> x
[3]
>>> y
[]
Ответ 2
Ни.
x = y = some_function()
эквивалентно
temp = some_function()
x = temp
y = temp
Обратите внимание на порядок. Самая левая цель назначается первой. (Аналогичное выражение в C может присваиваться в противоположном порядке.) Из документов на Назначение Python:
... присваивает единственный результирующий объект каждому из целевых списков слева направо.
Разборка показывает это:
>>> def chained_assignment():
... x = y = some_function()
...
>>> import dis
>>> dis.dis(chained_assignment)
2 0 LOAD_GLOBAL 0 (some_function)
3 CALL_FUNCTION 0
6 DUP_TOP
7 STORE_FAST 0 (x)
10 STORE_FAST 1 (y)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
ПРЕДОСТЕРЕЖЕНИЕ: тот же объект всегда присваивается каждой цели. Так как @Wilduck и @andronikus указывают, вы, вероятно, никогда этого не хотите:
x = y = [] # Wrong.
В приведенном выше случае x и y относятся к тому же списку. Поскольку списки mutable, добавление к x будет похоже, повлияет на y.
x = [] # Right.
y = []
Теперь у вас есть два имени, относящихся к двум различным пустым спискам.
Ответ 3
Что делать, если somefunction()
возвращает разные значения при каждом вызове?
import random
x = random.random()
y = random.random()
Ответ 4
В
x = somefunction()
y = somefunction()
somefunction
будет вызываться дважды вместо одного раза.
Даже если он возвращает один и тот же результат каждый раз, это будет заметно, если требуется вернуть минуту!
Или, если он имеет побочный эффект, например. запрашивая у пользователя пароль.
Ответ 5
Это приведет к тому же только в том случае, если функция не имеет побочных эффектов и возвращает один элемент детерминированным образом (учитывая его входы).
например:.
def is_computer_on():
return True
x = y = is_computer_on()
или
def get_that_constant():
return some_immutable_global_constant
Обратите внимание, что результат будет таким же, но процесс достижения результата не будет:
def slow_is_computer_on():
sleep(10)
return True
Содержание переменных x и y будет одинаковым, но инструкция x = y = slow_is_computer_on()
будет длиться 10 секунд, а ее аналог x = slow_is_computer_on() ; y = slow_is_computer_on()
будет длиться 20 секунд.
Было бы почти то же самое, если функция не имеет побочных эффектов и возвращает неизменяемость детерминированным образом (учитывая его входы).
например:.
def count_three(i):
return (i+1, i+2, i+3)
x = y = count_three(42)
Обратите внимание, что применяются те же уловы, которые описаны в предыдущем разделе.
Почему я говорю почти? Из-за этого:
x = y = count_three(42)
x is y # <- is True
x = count_three(42)
y = count_three(42)
x is y # <- is False
Хорошо, использование is
- это что-то странное, но это показывает, что возврат не является тем же. Это важно для изменяемого случая:
Это опасно и может привести к ошибкам, если функция возвращает изменчивый
В этом вопросе также был дан ответ. Для полноты, я воспроизвожу аргумент:
def mutable_count_three(i):
return [i+1, i+2, i+3]
x = y = mutable_count_three(i)
Поскольку в этом сценарии x
и y
являются одним и тем же объектом, выполнение операции типа x.append(42)
означает, что обе x
и y
содержат ссылку на список, который теперь имеет 4 элемента.
Было бы не так, если функция имеет побочные эффекты
Учитывая печать побочного эффекта (который я считаю действительным, но вместо этого можно использовать другие примеры):
def is_computer_on_with_side_effect():
print "Hello world, I have been called!"
return True
x = y = is_computer_on_with_side_effect() # One print
# The following are *two* prints:
x = is_computer_on_with_side_effect()
y = is_computer_on_with_side_effect()
Вместо печати это может быть более сложный или более тонкий побочный эффект, но факт остается фактом: метод вызывается один или два раза, что может привести к разному поведению.
Было бы не то же самое, если функция не детерминирована, учитывая ее входы
Возможно простой случайный метод:
def throw_dice():
# This is a 2d6 throw:
return random.randint(1,6) + random.randint(1,6)
x = y = throw_dice() # x and y will have the same value
# The following may lead to different values:
x = throw_dice()
y = throw_dice()
Но вещи, связанные с часами, глобальными счетчиками, системным материалом и т.д., разумны для того, чтобы быть недетерминированными с учетом ввода, и в этих случаях значение x
и y
может расходиться.