python:关于赋值语句的混淆:l=[a,b]=[0,1]?
在python 3.4交互提示中:python:关于赋值语句的混淆:l=[a,b]=[0,1]?,python,Python,在python 3.4交互提示中: >>> l = [a,b] = [0,1] >>> a is l[0] True >>> b is l[1] True >>> l[0] = 2 >>> a 0 >>> l [2, 1] 我假设下面的语句执行列表第一个元素的就地更改 l[0] = 2 既然变
>>> l = [a,b] = [0,1]
>>> a is l[0]
True
>>> b is l[1]
True
>>> l[0] = 2
>>> a
0
>>> l
[2, 1]
我假设下面的语句执行列表第一个元素的就地更改
l[0] = 2
既然变量a引用的是同一个对象,为什么它的值保持为0?这个赋值语句内部发生了什么?
a
和l[0]
是同一个对象的两个名称,但是当你说l[0]=2
时,你只是在重新定义l[0]
a
仍然引用与以前相同的对象;只是l[0]
发生了变化a
指的是一个对象,而不是一个位置。这是因为您实际上在那里创建了三个变量a
、b
和l
。只有最后一个是列表。以逐行格式出现的情况如下:
a = 0
b = 1
l = [a, b] #which means l = [0, 1], not really the same referencing element a, b
如果你查一下
a is l[0]
b is l[1]
两者都返回true
,因为它们的值相同,但同样是值,而不是引用
然后当你改变时:
l[0] = 2
只有
l[0]
中的值发生变化,它不会影响a
。最好是使用好的调试器工具进行检查,如PyCharm
。它在监视窗口中显示并内联变量的所有当前值。我认为混淆是由于初始赋值:
l = [a,b] = [0,1]
这相当于:
t = [0, 1]
l = [a, b] = t
t = [0, 1]
a = t[0]
b = t[1]
l = t
l = [0, 1]
a = l[0]
b = l[1]
这反过来相当于:
t = [0, 1]
l = [a, b] = t
t = [0, 1]
a = t[0]
b = t[1]
l = t
l = [0, 1]
a = l[0]
b = l[1]
这反过来相当于:
t = [0, 1]
l = [a, b] = t
t = [0, 1]
a = t[0]
b = t[1]
l = t
l = [0, 1]
a = l[0]
b = l[1]
其余的都是这样。最后一个例子根本不正确。由于小的
int
缓存,它会像OP一样工作,但是对于小的int
缓存范围之外的数字,最后一个示例中的代码不会生成makea is l[0]
true。其他三个例子有点正确(顺序可能不同,但最终的引用都会匹配),但最后一个例子非常误导。你被小的int
缓存愚弄了。尝试使用值1000和2000<代码>a=1000,然后b=2000
,然后l=[10002000]
。我会等的。@ShadowRanger是的,你说得对。我没有意识到相同的整数只适用于较小的值。谢谢你的信息。我更新了我的答案。@ShadowRanger这其实很微妙。如果执行a=1000
后接b=1000
,则a为b
为False
。但是如果你做了a=1000;b=1000
在同一行上,则a是b
是True
。似乎有一些每行缓存正在进行。它还解释了为什么1000是1000
是True
。是的。有一些有趣的错误关于每行常数折叠,CPython的人最近不得不修复。这里有一个有趣的例子,仍然适用于Python 3.5.1:a=lambda:True;b=λ:1
。正如您所料,a()
返回True
。但是b()
也是如此。它常量折叠两个lambda
s,因为字节码是相同的,并且每个常量表的值都是彼此相等的(bool
被实现为int
的子类,其中True
具有值1
,False
具有值0
)。他们测试的是平等而不是身份。有趣,对吧?你用的术语似乎。。。我觉得很尴尬a
和b
是分别存储对与l[0]
和l[1]
相同对象的引用的名称。基本上,a
和l[0]
都指向(引用)一个值为0
的单个对象,但是a
没有对l[0]
的引用,也没有对a
的l[0]
的引用。当您指定给l[0]
时,它会更改l[0]
指向的位置;它过去指向什么并不重要,因为该引用正在被替换。@programforjoy:好吧,我将其描述为“对同一对象的两个引用0
”,而不是“附加到”。以那种方式使用“attached”使我认为a
和l[0]
属于0
,但事实并非如此。在内部,0
“知道”有多少东西引用了它,但它不知道哪些东西引用了它。@Ian:不,它不是那样工作的。“字面价值”在这里不是一个有意义的概念;Python不像Java语言那样区分“原语”和“高级类型”。小的int
缓存在愚弄你。CPython维护一个IIRC,-5和260之间的所有int
的缓存(可能是256,必须进行双重检查)<代码>是是直接指针比较;当名称绑定到同一个对象时,它只返回True
。@Ian:这是一个常见的混淆源;我看到很多人对有误解。与int
和str
同时发生;使用str
,任何str
literal(可能仅低于某个大小,未检查详细信息)实际上都是相同的str
,这要感谢插入了literal字符串。所以你可以做a=“abc”
,b=“abc”
,并且a是b
是真的
。但是,如果创建相同的str
值而不使用文本(并且Python的窥视孔优化器不会在内部转换为文本),则可以说,b=''.join(“abc”)
,然后a是b
变成False
@Ian:真正的关键是在处理诸如None
和NotImplemented
(其中is
是最佳解决方案)之类的单例时,或者在对象标识出于某种原因实际上很重要时,基本上只使用is
。对象标识的一个不太常见的用法是,当您需要测试是否有人传递参数与未传递参数,但所有值都是合法的(包括无
)。您可以创建一个特殊的sentinel对象,sentinel