前言
先看这三行:
a = [1]; b = [1] print(a == b, a is b, a[0] is b[0])
跑出来:
True False True
其实整道题就绕着生活里一个特别简单的区别打转 —— 长得一样,和是同一个东西,是两码事。双胞胎长得一样,但不是同一个人。== 问「你俩长得一样吗」,is 问「你俩根本就是同一个吗」。
Python 里数字、列表、元组在这件事上表现不太一样,容易混。所以下面按这三样分开看:先看整数 ,再看装它的列表,最后换成元组。
正文
整数
Python 3.15 把这份「提前造好的整数名单」扩大了:从 -5~256 改成 -5~1024。
Python一启动时就把预定的整数进行缓存,上面有写范围。之后谁要用 ,发的都是那一份:
x = 1 y = 1 print(x is y, x == y)
输出:
True True
数字又不会变,大家一起用一份没任何问题。
is 比的是每样东西自带的编号,id() 能看到:
print(id(x) == id(y))
输出:
True
id() 打出来是一长串数字,而且每次运行都不一样,别去记它,看两个编号「相不相等」就够了。
但别拿 is 比数字
刚才那个 True,只是 Python 顺手做的省事优化,不是规矩。换个数字立刻翻车:
x = 1024
y = int("1024")
print(x == y, x is y)输出:
#Python 3.15之前 True False #Python 3.15之后 True Ture
再来一个
x = 1025
y = int("1025")
print(x == y, x is y)输出:
True False
那 is 留着干嘛
日常几乎只剩一个用处:判断 None。
x = None print(x is None)
输出:
True
None 整个程序里就一份,问「是不是同一个」最干脆。写 x == None 多数时候也能跑,但「什么叫相等」是可以被别人改的,is 改不了,所以大家都写 is None。True 和 False 也各只有一份,不过平时直接写 if flag: 更顺手。
列表
写两次 [1],Python 就老老实实造两个新列表 —— 像你把同一份购物清单抄了两遍,纸是两张,内容一模一样:
a = [1]; b = [1] print(a == b, a is b, a[0] is b[0])
输出:
True False True
== 逐项比内容,所以是 True;两张纸当然不是同一张,所以 is 是 False。那第三项为什么又 True 了?因为列表里装的不是 1 本身,是「指到 1 的箭头」,两张纸上的箭头都指到上一节那个共用的 1。
列表不能像整数那样共用,原因也很直白:列表能改,两个人共用一份会互相捣乱。想让两个名字指着同一张纸,得直接 b = a:
a = [1] b = a b.append(2) print(a, b, a is b)
输出:
[1, 2] [1, 2] True
a 也跟着变了,因为它俩压根就是同一个列表。只想复制内容的话用 copy():
a = [1] c = a.copy() c.append(2) print(a, c, a is c)
输出:
[1] [1, 2] False
说白了就一句:能改的东西各造各的,不能改的东西可以共用。
元组
把列表换成元组,答案基本没变:
a = tuple([1, 2]) b = tuple([1, 2]) print(a == b, a is b, a[0] is b[0])
输出:
True False True
元组只是「自己改不了」,Python 并没有规定内容一样的元组必须共用一份,所以这儿照样是两个元组、里面共用一个 1。
不过元组比列表多几个小意外。
一、字面量写法可能被偷偷合并
a = (1, 2) b = (1, 2) print(a is b)
写在同一个 .py 文件里跑,CPython 常见输出:
True
编译器发现两次写的是一模一样的常量,就只造一份,两个名字都指过去。但在交互式里一行行敲(每行单独编译),通常又是 False:
>>> a = (1, 2) >>> b = (1, 2) >>> a is b False
空元组更彻底,整个程序里就一份:
print(() is ())
输出:
True
这些都是 Python 自己省事的做法,不是规矩。
二、元组里装的东西决定第三项
装小整数时,元素还是共用的那份;换成列表,就各造各的了:
t1 = ([1],) t2 = ([1],) print(t1 == t2, t1 is t2, t1[0] is t2[0])
输出:
True False False
顺带破一个常见误会:「元组不可变」说的是元组里装的那些箭头不能换,不是里面的东西不能动:
t = ([1],) t[0].append(2) print(t)
输出:
([1, 2],)
t[0] = 其它 会报错,但 t[0].append(2) 没问题。正因为里面可能装着能改的列表,Python 也不能随便把两个元组合并成一份。
还有一点,类型不一样就不相等:
print((1, 2) == [1, 2])
输出:
False
内容看着一样,但一个是元组一个是列表,== 不认。
小结
- == 问是否相等(你俩长得一样吗?)
- is 问是否是同一个(你俩根本就是同一个吗)
参考文章
- AI
- Python 文档
