Подтвердить что ты не робот

Как работают цепочки назначений?

Цитата из чего-то:

>>> x = y = somefunction()

совпадает с

>>> y = somefunction()
>>> x = y

Вопрос: есть ли

x = y = somefunction()

то же, что и

x = somefunction()
y = somefunction()

?

Исходя из моего понимания, они должны быть одинаковыми, потому что somefunction может возвращать только одно значение.

4b9b3361

Ответ 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 может расходиться.