提问人:q0987 提问时间:9/30/2011 最后编辑:agfq0987 更新时间:3/29/2022 访问量:23031
链式分配如何工作?
How do chained assignments work?
问:
引自某事:
>>> x = y = somefunction()
与
>>> y = somefunction()
>>> x = y
问题:是
x = y = somefunction()
同上
x = somefunction()
y = somefunction()
?
根据我的理解,它们应该是相同的,因为只能返回一个值。somefunction
答:
如果每次调用时都返回不同的值怎么办?somefunction()
import random
x = random.random()
y = random.random()
评论
如果返回可变值,它们的工作方式不一定相同。考虑:somefunction
>>> def somefunction():
... return []
...
>>> x = y = somefunction()
>>> x.append(4)
>>> x
[4]
>>> y
[4]
>>> x = somefunction(); y = somefunction()
>>> x.append(3)
>>> x
[3]
>>> y
[]
评论
x is y
x = y = []
x
y
x
y
x=y=[]; y=3; print(x)
[]
3
x
y
在
x = somefunction()
y = somefunction()
somefunction
将被调用两次而不是一次。
即使它每次都返回相同的结果,如果返回结果需要一分钟,这将是一个明显的问题! 或者,如果它有副作用,例如要求用户输入密码。
左先
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 指的是同一个列表。因为列表是可变的,所以附加到 x 似乎会影响 y。
x = [] # Right.
y = []
现在,您有两个名称引用两个不同的空列表。
评论
x = y = []
temp = []
x = temp
y = temp
x = y = []
x = x.a = y
x.a = y; x = y
x = y; x.a = y
x.a = x = y
只有当函数没有副作用并以确定性方式返回单例(给定其输入)时,它才会产生相同的结果。
例如:
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 变量的内容是相同的,但指令将持续 10 秒,而其对应的指令将持续 20 秒。x = y = slow_is_computer_on()
x = slow_is_computer_on() ; y = slow_is_computer_on()
如果函数没有副作用并以确定性方式返回不可变(给定其输入),则几乎相同。
例如:
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)
因为在这种情况下,并且是同一个对象,执行像 whould 这样的操作意味着两者都包含对现在有 4 个元素的列表的引用。x
y
x.append(42)
x
y
如果功能有副作用,那就不一样了
将打印视为副作用(我认为这有效,但可以使用其他示例来代替):
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
正如 Bob Stein 已经说过的那样,分配顺序很重要;看看下面这个非常有趣的案例:
L = L[1] = [42, None]
现在,包含什么?您必须了解,最初分配给 ;最后,执行类似的事情。因此,你创建了一些循环的无限“列表”(这里的“列表”这个词更类似于Lisp中的一些,标量是,列表本身是)。L
[42, None]
L
L[1] = L
CONS
42
CAR
CDR
只需输入:
>>> L
[42, [...]]
然后通过打字来找点乐子,然后,然后,直到你到达最后......L[1]
L[1][1]
L[1][1][1]
结论
这个例子比其他答案中的其他例子更难理解,但另一方面,你可以更快地看到这一点
L = L[1] = [42, None]
和
L[1] = L = [42, None]
因为如果之前未定义,第二个将引发异常,而第一个将始终有效。L
评论
python
python-3.x