链式作业如何运作?

q09*_*987 29 python python-3.x

来自某事的引用:

>>> x = y = somefunction()
Run Code Online (Sandbox Code Playgroud)

是相同的

>>> y = somefunction()
>>> x = y
Run Code Online (Sandbox Code Playgroud)

问题:是

x = y = somefunction()
Run Code Online (Sandbox Code Playgroud)

同样的

x = somefunction()
y = somefunction()
Run Code Online (Sandbox Code Playgroud)

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

Bob*_*ein 49

都不是.

x = y = some_function()
Run Code Online (Sandbox Code Playgroud)

相当于

temp = some_function()
x = temp
y = temp
Run Code Online (Sandbox Code Playgroud)

记下订单.最左边的目标首先被分配.(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
Run Code Online (Sandbox Code Playgroud)

注意:始终为每个目标分配相同的对象.所以@Wilduck和@andronikus指出,你可能永远不会想要这个:

x = y = []   # Wrong.
Run Code Online (Sandbox Code Playgroud)

在上面的情况中,x和y表示相同的列表.因为列表是可变的,所以附加到x 似乎会影响 y.

x = []   # Right.
y = []
Run Code Online (Sandbox Code Playgroud)

现在您有两个名称引用两个不同的空列表.

  • @grofte 不确定在重读几次后我是否得到了你的例子。但我建议**棘手的简洁并不好**。代码应该是人类可读的。副作用应该被重新转化为明显、明确的后果。你未来的大脑是一个温柔的东西。 (3认同)
  • 很好的解释。我发现这种行为不直观,并且被它烧伤了。我有 `x = xa = y`,期望它表现得像 `xa = y; x = y`,但由于赋值顺序,它实际上执行了 `x = y; xa = y` 失败得相当可怕。我想我可以通过 `xa = x = y` 来获得所需的行为,但这似乎确实误导了我。 (2认同)

Wil*_*uck 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
[]
Run Code Online (Sandbox Code Playgroud)

  • 是的,非常好.当我想要懒惰并且做'x = y = []`时,我不止一次遇到麻烦.如果`x`和`y`应该是不同的列表,那么这就是错误啊. (4认同)
  • 接得好.即使它是一个不可变的值,`x is y`也会不同(保存令人讨厌的陷阱,比如字符串文字和小整数的缓存). (2认同)
  • @JiapengZhang 这不完全正确。“x”和“y”都将引用同一个底层对象,但彼此不同。考虑`x=y=[]; y=3;打印(x)`。它将打印“[]”,而不是“3”,因为名称“x”和“y”之间没有联系,只有底层值。 (2认同)

Gre*_*ill 14

如果somefunction()每次调用它会返回不同的值会怎么样?

import random

x = random.random()
y = random.random()
Run Code Online (Sandbox Code Playgroud)

  • 关键是你必须做出相反的假设,即函数总是返回相同的东西,对于你所说的持有. (8认同)

Tho*_*hel 7

正如鲍勃·斯坦因(Bob Stein)已经说过的,分配的顺序很重要;看看下面这个非常有趣的案例:

L = L[1] = [42, None]
Run Code Online (Sandbox Code Playgroud)

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

只需输入:

>>> L
[42, [...]]
Run Code Online (Sandbox Code Playgroud)

然后输入一些乐趣L[1],然后L[1][1],然后,L[1][1][1]直到到达终点......

结论

这个例子比其他答案中的其他例子更难理解,但另一方面,你可以更快地看到

L = L[1] = [42, None]
Run Code Online (Sandbox Code Playgroud)

不等于

L[1] = L = [42, None]
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*ram 5

当函数没有副作用并以确定性方式返回单例(给定其输入)时,结果才会相同。

例如:

def is_computer_on():
    return True

x = y = is_computer_on()
Run Code Online (Sandbox Code Playgroud)

要么

def get_that_constant():
    return some_immutable_global_constant
Run Code Online (Sandbox Code Playgroud)

请注意,结果将是相同的,但是获得结果的过程将不会:

def slow_is_computer_on():
    sleep(10)
    return True
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

请注意,适用上一节中说明的相同捕获。

为什么我说差不多?因为这:

x = y = count_three(42)
x is y  # <- is True

x = count_three(42)
y = count_three(42)
x is y  # <- is False
Run Code Online (Sandbox Code Playgroud)

好的,使用is有点奇怪,但这说明返回的结果是不一样的。这对于可变情况很重要:

如果该函数返回可变值,则很危险,并可能导致错误

这个问题也得到了回答。为了完整起见,我重播该参数:

def mutable_count_three(i):
    return [i+1, i+2, i+3]

x = y = mutable_count_three(i)
Run Code Online (Sandbox Code Playgroud)

因为在那种情况下x,并y是同一个对象,做这样的操作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()
Run Code Online (Sandbox Code Playgroud)

它可能会带来更复杂或更微妙的副作用,而不是打印出来,但事实仍然存在:该方法被调用一次或两次,并且可能导致不同的行为。

如果函数在给定其输入的情况下是不确定的,那就会不同

也许是一个简单的随机方法:

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()
Run Code Online (Sandbox Code Playgroud)

但是,涉及到时钟,全局计数器,系统的东西,等东西是明智的是非确定性给出的输入,在这些情况下的价值xy可能发散。