链式赋值如何进行?
- 2025-01-17 09:23:00
- admin 原创
- 94
问题描述:
引用某件事:
>>> x = y = somefunction()
等同于
>>> y = somefunction()
>>> x = y
问题:
x = y = somefunction()
和...一样
x = somefunction()
y = somefunction()
?
根据我的理解,它们应该是相同的,因为somefunction
只能返回一个值。
解决方案 1:
先左转
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 = []
现在您有两个名称引用两个不同的空列表。
解决方案 2:
somefunction
如果返回可变值,它们的工作方式不一定相同。考虑一下:
>>> def somefunction():
... return []
...
>>> x = y = somefunction()
>>> x.append(4)
>>> x
[4]
>>> y
[4]
>>> x = somefunction(); y = somefunction()
>>> x.append(3)
>>> x
[3]
>>> y
[]
解决方案 3:
somefunction()
如果每次调用时返回不同的值会怎样?
import random
x = random.random()
y = random.random()
解决方案 4:
仅当函数没有副作用并且以确定性的方式返回单例(给定其输入)时,才会产生相同的结果。
例如:
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
可能会有所不同。
解决方案 5:
正如 Bob Stein 所说,分配的顺序很重要;请看下面这个非常有趣的案例:
L = L[1] = [42, None]
现在,包含什么L
?您必须了解,最初是单个对象[42, None]
,它被分配给L
;最后,L[1] = L
执行类似的事情。因此,您创建了一些循环无限“列表”(这里的“列表”一词更类似于CONS
Lisp 中的一些列表,其中标量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
而第一个始终有效。
解决方案 6:
在
x = somefunction()
y = somefunction()
somefunction
将被调用两次而不是一次。
即使每次都返回相同的结果,如果返回结果需要一分钟,这也会很明显!或者如果它有副作用,例如要求用户输入密码。
扫码咨询,免费领取项目管理大礼包!