翻译:老齐
与本文内容配套的图书:《跟老齐学Python:轻松入门》《Python大学实用教程》,各大电商平台有售。
Python中的
is
和
==
是不一样的。使用
is
可以比较数字,代码也正常运行。也有人说
is
比
==
要更快,或者你可能觉得它看起来更像Python。然而,重要的是要记住这些运算符的行为并不完全相同。
==
用于比较两个对象的值是否相等,而
is
检查两个变量是否指向内存中的同一个对象。在大多数情况下,这意味着你应该使用
==
和
!=
,除非与
None
进行比较。
在本文中,你将学习:
- 对象相等和同一性的区别是什么
-
何时使用
==
和is
比较对象 - 这些Python运算符的原理是什么
-
为什么使用
is
和is not
比较值会导致意外 -
如何编写自定义的
__eq__()
类方法来定义相等运算符行为
介绍
is
和
is not
的应用
is
和
is not
用来比较两个对象。在CPython中,比较的是对象的内存地址。Python中的一切都是对象,每个对象都存储在特定的内存位置,
is
和
is not
'检查两个变量是否引用内存中的同一个对象。
注意: 记住,具有相同值的对象可能存储在不同的内存地址中。
你可以使用
id()
来检查一个对象的内存地址:
>>> help(id)
Help on built-in function id in module builtins:
id(obj, /)
Return the identity of an object.
This is guaranteed to be unique among simultaneously existing objects.
(CPython uses the object's memory address.)
>>> id(id)
2570892442576
复制代码
最后一行显示存储内置函数
id
本身的内存地址。
通常,具有相同值的对象在默认情况下具有相同的id。例如,数字-5到256在CPython中被保存,每个数字都存储在内存中单一且固定的位置,这为常用整数节省了内存。
你可以使用
sys.intern()
来保存字符串以提高性能,此函数允许你比较它们的内存地址,而不是对字符串里的字符进行逐个比较:
>>> from sys import intern
>>> a = 'hello world'
>>> b = 'hello world'
>>> a is b
False
>>> id(a)
1603648396784
>>> id(b)
1603648426160
>>> a = intern(a)
>>> b = intern(b)
>>> a is b
True
>>> id(a)
1603648396784
>>> id(b)
1603648396784
复制代码
变量
a
和
b
最初指向内存中的两个不同对象,如它们的不同id所示。使用
intern
后,
a
和
b
则指向内存中的同一对象。在原来的操作中,两个
'hello world'
分别在新的内存位置创建对象,但是,对同样的字符串执行
intern
后,后面所创建的字符串所指向的内存地址与第一个
'hello world'
的内存地址相同。
注意:即使对象的内存地址在任何给定的时间都是唯一的,但这个内存地址在同一代码的不同运行过程中是不同的,并且取决于CPython的版本和运行代码的计算机。
默认情况下,具有
intern
效果的对象是
None
、
True
、
False
和简单字符串。请记住,大多数情况下,具有相同值的不同对象将存储在不同的内存地址中,这意味着你不应该使用
is
来比较值。
存储整数
Python将常用的值(例如,整数-5到256)默认保存在内存中,从而节省内存开支。下面的代码向你展示了为什么只有一些整数具有固定的内存地址:
>>> a = 256
>>> b = 256
>>> a is b
True
>>> id(a)
1638894624
>>> id(b)
1638894624
>>> a = 257
>>> b = 257
>>> a is b
False
>>> id(a)
2570926051952
>>> id(b)
2570926051984
复制代码
最初,
a
和
b
引用内存中的同一个存储对象,但当它们的值超出常用整数的范围(从-5到256)时,它们就存储在不同的内存地址中。
当多个变量引用同一对象时
用赋值运算符(
=
)使一个变量等于另一个变量时,可以使这些变量指向内存中的同一对象。这可能会导致可变对象出现意外行为: