链式作业如何运作?

时间:2011-09-29 18:34:45

标签: python python-3.x

引自某事:

>>> x = y = somefunction()

相同
>>> y = somefunction()
>>> x = y

问题:是

x = y = somefunction()

相同
x = somefunction()
y = somefunction()

根据我的理解,它们应该相同,因为somefunction只能返回一个值。

6 个答案:

答案 0 :(得分:41)

都不是。

x = y = some_function()

相当于

temp = some_function()
x = temp
y = temp

请注意订单。 首先分配最左边的目标。 (may中的C opposite order中的类似表达式。)来自Python assignment上的文档:

  

...从左到右将单个结果对象分配给每个目标列表。

反汇编显示:

>>> 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

注意:same object始终分配给每个目标。所以@Wilduck和@andronikus指出,你可能永远不会想要这个:

x = y = []   # Wrong.

在上面的情况中,x和y表示相同的列表。由于列表为mutable,因此附加到x将seem to affect y。

x = []   # Right.
y = []

现在你有两个名字引用两个不同的空列表。

答案 1 :(得分:35)

如果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 :(得分:13)

如果somefunction()每次调用时返回不同的值,该怎么办?

import random

x = random.random()
y = random.random()

答案 3 :(得分:4)

x = somefunction()
y = somefunction()

somefunction将被调用两次而不是一次。

即使每次都返回相同的结果,如果返回结果需要一分钟,这将是一个明显的问题! 或者如果它有副作用,例如询问用户他的密码。

答案 4 :(得分:4)

如果函数没有副作用并且以确定的方式返回单例(给定其输入),它将导致相同的

E.g:

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秒。

如果函数没有副作用并且以确定的方式返回不可变(给定其输入),它将几乎相同。

E.g:

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)

因为在那种情况下xy是同一个对象,所以执行x.append(42)这样的操作意味着xy都会有参考现在有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()

但是,与时钟,全局计数器,系统内容等相关的事情对于输入是不确定的是明智的,在这些情况下xy的值可能不同。

答案 5 :(得分:0)

正如鲍勃·斯坦(Bob Stein)所说,分配顺序很重要;看一下以下非常有趣的情况:

L = L[1] = [42, None]

现在,什么包含L?您必须了解,最初分配给[42, None]的单个对象是L;最后,执行类似L[1] = L的操作。因此,您创建了一些循环的无限“列表”(此处的“列表”一词与Lisp中的某些CONS类似,标量42CAR,列表本身为CDR

只需输入:

>>> L
[42, [...]]

然后输入L[1],然后依次输入L[1][1]L[1][1][1],直到结束为止,都会很有趣。

结论

该示例比其他答案中的其他示例更难以理解,但另一方面,您可以更快地看到它

L = L[1] = [42, None]

不同
L[1] = L = [42, None]

因为如果先前未定义L,则第二个将引发异常,而第一个将始终有效。