python not is函数_【译】Python中的 !=与is not不同

翻译:老齐

与本文内容配套的图书:《跟老齐学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)时,它们就存储在不同的内存地址中。

当多个变量引用同一对象时

用赋值运算符(=)使一个变量等于另一个变量时,可以使这些变量指向内存中的同一对象。这可能会导致可变对象出现意外行为:

>>> a = [1, 2, 3]

>>> b = a

>>> a

[1, 2, 3]

>>> b

[1, 2, 3]

>>> a.append(4)

>>> a

[1, 2, 3, 4]

>>> b

[1, 2, 3, 4]

>>> id(a)

2570926056520

>>> id(b)

2570926056520

刚才发生了什么? 你向a 添加了一个新元素,但是现在b也包含了这个元素! 在b = a这一行,设置b指向与a相同的内存地址,这样两个变量就都引用相同的对象。

如果你独立地定义这些列表,那么它们就被存储在不同的内存地址中,并独立地运行:

>>> a = [1, 2, 3]

>>> b = [1, 2, 3]

>>> a is b

False

>>> id(a)

2356388925576

>>> id(b)

2356388952648

因为a和b现在引用内存中的不同对象,所以更改一个对象不会影响另一个对象。

用==和!=比较对象

回想一下,具有相同值的对象通常存储在不同的内存地址中。如果要检查两个对象是否具有相同的值,而不管它们存储在内存中的位置,使用运算符=和!=。在绝大多数情况下,这就是你想做的。

当对象副本相等但不相同时

在下面的示例中,b是a的副本(a是可变对象,如列表或字典)。两个变量都有相同的值,但它们将各自存储在不同的内存地址:

>>> a = [1, 2, 3]

>>> b = a.copy()

>>> a

[1, 2, 3]

>>> b

[1, 2, 3]

>>> a == b

True

>>> a is b

False

>>> id(a)

2570926058312

>>> id(b)

2570926057736

a 和 b 现在存储在不同的内存地址,因此a is b不再返回True。但是,a==b返回True,因为两个对象具有相同的值。

相等比较如何起作用

==的魔力体现在该符号左边对象所具有的__eq__()方法中。

这是一个神奇的类方法,每当这个类的一个实例与另一个对象进行比较时都会调用它。如果未实现此方法,则默认情况下==比较两个对象的内存地址。

作为练习,创建一个继承str的SillyString类并实现__eq__() ,以比较此字符串的长度是否与另一个对象的长度相同:

class SillyString(str):

# This method gets called when using == on the object

def __eq__(self, other):

print(f'comparing {self} to {other}')

# Return True if self and other have the same length

return len(self) == len(other)

现在,用'hello world'创建的SillyString实例应该等于用'world hello'创建的实例,甚至等于长度相同的任何其他对象:

>>> # Compare two strings

>>> 'hello world' == 'world hello'

False

>>> # Compare a string with a SillyString

>>> 'hello world' == SillyString('world hello')

comparing world hello to hello world

True

>>> # Compare a SillyString with a list

>>> SillyString('hello world') == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

comparing hello world to [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

True

当然,对于一个字符串形式的对象来说,这是愚蠢的行为,但它确实说明了当你使用==比较两个对象时会发生什么。对于!=,则是通过实现特定的__ne__()方法,给出逆响应。

上面的示例还清楚地向你展示了为什么更好的做法是用 is来比较None,而不是使用==运算符。is比较的是内存地址,所以它不仅更快,而且更安全,因为它不依赖于任何__eq__()方法的逻辑。

比较的比较

根据经验,你应该常用== 和!=,除非与None进行比较:使用==和!=比较对象的相等性。这里,你通常比较两个对象的值。如果要比较两个对象是否具有相同的内容,而不关心它们存储在内存中的位置,则需要下面的做法。

如果要比较对象的唯一标识,请使用is和is not。这里,你要比较两个变量是否指向内存中的同一个对象。这些运算符的主要用例是与None进行比较。与使用类方法相比,按内存地址与None进行比较更快、更安全。

具有相同值的变量通常存储在不同的内存地址中,这意味着你应该使用== 和!=来比较他们的值。只有当你想检查两个变量是否指向同一个内存地址时,才使用is和is not。

结论

在本文中,你了解了== 和!=比较两个对象的值,而is和is not比较两个变量是否引用内存中的同一个对象。如果你牢记这一区别,那么应该能够防止代码中出现意外行为。

你还可以看看如何使用sys.intern()来优化字符串的内存使用和比较,尽管Python可能已经在幕后自动为你处理了这一问题。

现在你已经了解了两种比较的幕后操作,可以尝试编写自己的__eq__()方法,这些方法定义了在使用==运算符时如何比较该类的实例。去应用关于Python比较运算符的这些新知识吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值