交换 Python 中的变量和列表元素出乎意料
Swapping variables and list elements in Python works unexpectedly
我刚刚想出了一个代码示例:
ls = [2222, 1111]
a = ls[0]
print(a is ls[0])
a, ls[1] = ls[1], a
print(ls)
打印:
True
[2222, 2222]
我的问题是,为什么在上述情况下 a
与 ls[0]
不是同一个对象? a is ls[0]
检查是 True
,因此它必须与:
相同
ls[0], ls[1] = ls[1], ls[0]
但事实并非如此。后一个产生 [1111, 2222]
。这是什么谜?
您没有设置 ls[0]!
正在修复:
ls = [2222, 1111]
# Assigns ls[0] to a, but not changes ls[0]!
a = ls[0]
print(a is ls[0])
# Assigns ls[1] to a and a to ls[1]. At this point ls[1] is a!
a, ls[1] = ls[1], a
print(ls)
# Now ls[0] is assigned!
ls[0] = a
print(ls)
输出:
True
[2222, 2222]
[1111, 2222]
顺便说一句:您可以通过以下方式进行反向列表:
ls = [2222, 1111]
print(ls[::-1])
输出:
[1111, 2222]
此代码将不起作用,因为在这种情况下您引用了 ls[0]
处的对象,而不是对 ls[0]
.
的内存位置的引用
从字面上看,a
和 ls[1]
都不是对象:它们分别是 identifier and subscription。虽然它们在表达式中使用时 评估为 对象,但这并不会使它们与这些对象相同——并且“它们的”对象的身份也不会扩展到它们。
- 表达式
a is ls[0]
的意思是“计算a
和ls[0]
并比较结果的同一性”。它不检查标识符 a
是否与订阅 ls[0]
. 相同
值得注意的是,当在 assignment statement 中使用时,a
和 ls[1]
都不代表“查找对象的表达式”,而是“分配对象的目标”到”。前者的特点不影响后者
语句 a, ls[1] = ls[1], a
同时使用 a
和 ls[1]
作为表达式和目标:
右边是一个表达式。它表示“评估 ls[1]
和 a
的结果的元组”。
左边是作业。它表示“分配给标识符a
和订阅ls[1]
”。
标识符的赋值始终是即时的:标识符之前引用的值是什么并不重要。具体来说,a
之前引用了一个也被 ls[0]
引用的对象并不重要,它的新指代也被 ls[1]
引用并不重要。
要理解为什么这种区别很重要,请考虑标识符与文字“相同”的情况:
>>> # integers < 256 are interned
>>> a = 12
>>> # identifier "is identical to" literal
>>> a is 12
True
如果表达式中的同一性等同于赋值中的同一性,那么设置 a = 13
现在会重新分配 a
和 12
以具有值 13
.
简单赋值对目标的前值没有影响。
之后
ls = [2222, 1111]
a = ls[0]
a
和ls
的第一个元素都是对整数2222
的引用。
作业
a, ls[1] = ls[1], a
实际上与
相同
t = ls[1], a # The tuple (1111, 2222)
a = t[0] # Set a to t[0] == 1111
ls[1] = t[1] # Set ls[1] to t[1] == 2222
您从未修改过 ls
的第一个元素;您只更改了 a
所指的内容以及 ls
的第二个元素所指的内容。您可以看到 a
现在指的是 1111
,因为这就是 ls[1]
的值 在 ls[1]
被修改之前。
>>> print(a)
1111
我刚刚想出了一个代码示例:
ls = [2222, 1111]
a = ls[0]
print(a is ls[0])
a, ls[1] = ls[1], a
print(ls)
打印:
True
[2222, 2222]
我的问题是,为什么在上述情况下 a
与 ls[0]
不是同一个对象? a is ls[0]
检查是 True
,因此它必须与:
ls[0], ls[1] = ls[1], ls[0]
但事实并非如此。后一个产生 [1111, 2222]
。这是什么谜?
您没有设置 ls[0]!
正在修复:
ls = [2222, 1111]
# Assigns ls[0] to a, but not changes ls[0]!
a = ls[0]
print(a is ls[0])
# Assigns ls[1] to a and a to ls[1]. At this point ls[1] is a!
a, ls[1] = ls[1], a
print(ls)
# Now ls[0] is assigned!
ls[0] = a
print(ls)
输出:
True
[2222, 2222]
[1111, 2222]
顺便说一句:您可以通过以下方式进行反向列表:
ls = [2222, 1111]
print(ls[::-1])
输出:
[1111, 2222]
此代码将不起作用,因为在这种情况下您引用了 ls[0]
处的对象,而不是对 ls[0]
.
从字面上看,a
和 ls[1]
都不是对象:它们分别是 identifier and subscription。虽然它们在表达式中使用时 评估为 对象,但这并不会使它们与这些对象相同——并且“它们的”对象的身份也不会扩展到它们。
- 表达式
a is ls[0]
的意思是“计算a
和ls[0]
并比较结果的同一性”。它不检查标识符a
是否与订阅ls[0]
. 相同
值得注意的是,当在 assignment statement 中使用时,a
和 ls[1]
都不代表“查找对象的表达式”,而是“分配对象的目标”到”。前者的特点不影响后者
语句 a, ls[1] = ls[1], a
同时使用 a
和 ls[1]
作为表达式和目标:
右边是一个表达式。它表示“评估
ls[1]
和a
的结果的元组”。左边是作业。它表示“分配给标识符
a
和订阅ls[1]
”。
标识符的赋值始终是即时的:标识符之前引用的值是什么并不重要。具体来说,a
之前引用了一个也被 ls[0]
引用的对象并不重要,它的新指代也被 ls[1]
引用并不重要。
要理解为什么这种区别很重要,请考虑标识符与文字“相同”的情况:
>>> # integers < 256 are interned
>>> a = 12
>>> # identifier "is identical to" literal
>>> a is 12
True
如果表达式中的同一性等同于赋值中的同一性,那么设置 a = 13
现在会重新分配 a
和 12
以具有值 13
.
简单赋值对目标的前值没有影响。
之后
ls = [2222, 1111]
a = ls[0]
a
和ls
的第一个元素都是对整数2222
的引用。
作业
a, ls[1] = ls[1], a
实际上与
相同t = ls[1], a # The tuple (1111, 2222)
a = t[0] # Set a to t[0] == 1111
ls[1] = t[1] # Set ls[1] to t[1] == 2222
您从未修改过 ls
的第一个元素;您只更改了 a
所指的内容以及 ls
的第二个元素所指的内容。您可以看到 a
现在指的是 1111
,因为这就是 ls[1]
的值 在 ls[1]
被修改之前。
>>> print(a)
1111