一文了解 Python 中的对象比较方法 is 和 == 及其本质(下)

简介: 我们在学习基础的时候没听说 Python 有 C 或 C++ 中的指针啊,Python 中指针是什么?先把指针这个概念放一放,一提到指针可能初学 C 和 C++ 的人都害怕(本人也害怕),先来理解一下 Python 里面对象的本质。

== 运算符的本质

当您编写 a == b 时,实际上是在调用一个魔术方法(magic method),也称为 dunder 方法(函数名的两边的都有下划线,the double-underscore)。日常学习中你看能已经见过一些魔术方法,例如:


  • __init__ 方法:在初始化 Python 类的实例时调用
  • __str__ 方法:使用 str 函数时调用,例如 str(xxx)
  • __repr__ 方法,类似于 __str__ 但在其他情况下也会调用,例如错误消息


魔术方法只是 Python 类上的方法,双下划线表示它们与内置的 Python 函数交互。例如,在 Python 类上重写 __str__ 方法会改变 str 函数在该修改类的实例上调用时的行为方式。


当谈到 == 和 __eq__ 时,通过一些例子最容易理解。让我们来看看!

重写 __eq_ 方法

下面,我们用自己的 __eq__ 方法定义一个类。每个 __eq__ 方法都有两个参数:


  • self,所讨论的类的实例
  • 第二个参数(在下面的代码片段中,我们定义为 other),它是与 self 进行比较的任何对象。
class MyEqualClass:
  def __eq__(self, other):
        return self is other


然后进行测试:

>>> class MyEqualClass:
  def __eq__(self, other):
            return self is other
>>> a = MyEqualClass()
>>> a == 'Hello World'
False
>>> 


然后这会调用 MyEqualClass 的 __eq__ 方法,其中 a 作为 self 参数,字符串 'Hello World' 作为另一个参数。


在上面的例子中,MyClass.__eq__ 使用了 is 比较器,相当于比较每个对象的 id。这实际上是 Python 中任何用户定义类的默认行为。


如果我们想自定义一个类,所以对比的对象都相等,就可以这样写:

class MyAlwaysTrueClass:
    def __init__(self, name):
        self.name = name
    def __eq__(self, other):
      return True


因为我们重写了 __eq__ 方法以始终返回 True,这意味着在 == 比较器下,此类的所有实例都将被视为相等,即使它们的名称具有不同的值!

>>> jane = MyAlwaysTrueClass("Jane")
>>> bob = MyAlwaysTrueClass("Bob")
>>> jane.name == bob.name
False
>>> jane == bob
True


此外,与不同类型的对象相比,我们还可以看到 MyAlwaysTrueClass 的实例表现得很奇怪:

>>> jane = MyAlwaysTrueClass("Jane")
>>> jane == "some string"
True
>>> jane == None
True
>>> jane == True
True


当将 MyAlwaysTrueClass 的实例与任何东西(不仅仅是同一个类的实例)进行比较时,它将返回 True。这是因为,正如我们之前所说,__eq__ 方法默认情况下不检查被比较对象的类型。


那如果我们写一个功能相反的 __eq__ 的 MyAlwaysFalseClass 呢?

class MyAlwaysFalseClass:
    def __init__(self, name):
      self.name = name
    def __eq__(self, other):
      return False


你可能觉得这很合理,但是:

>>> class MyAlwaysFalseClass:
    def __init__(self, name):
      self.name = name
    def __eq__(self, other):
          return False
>>> a = MyAlwaysFalseClass("name")
>>> a == a
False


上述的列子,a == a 的结果是 False,那是因为我们在任何类型作对比中都会返回 Flase


此外,由于 __eq__ 的行为取决于哪个对象是 self 哪个是 other,我们可以通过使用 MyAlwaysTrueClass 的一个实例和一个 MyAlwaysFalseClass 的实例来获得以下行为:

>>> jane = MyAlwaysTrueClass("Jane")
>>> bob = MyAlwaysFalseClass("Bob")
>>> jane == bob
True
>>> bob == jane
False
>>> 


这是因为我们比较 jane 和 bob 的顺序很重要。当我们执行 jane == bob 时,我们使用 MyAlwaysTrueClass 中定义的 __eq__ 方法:jane 是 self 而 bob 是 other,并且该方法总是返回 True。当我们写 bob == jane 时,情况正好相反,所以我们得到该表达式的 False 值。


总而言之:魔术方法是一种有趣的方式,它可以让 Python 做一些看起来很奇怪的事情,并且应该自行承担修改的风险!

4 总结

所以我们学了什么?我们已经介绍了 is== 这两个 Python 中的相等概念,并了解到


  • is 的本质使用对象 id;
  • == 使用 __eq__ 魔术方法。


我们还探索了如何覆盖 __eq__ 来自定义相等概念,并了解为什么这样做会导致一些奇怪的行为。


相关文章
|
22天前
|
测试技术 API Python
【10月更文挑战第1天】python知识点100篇系列(13)-几种方法让你的电脑一直在工作
【10月更文挑战第1天】 本文介绍了如何通过Python自动操作鼠标或键盘使电脑保持活跃状态,避免自动息屏。提供了三种方法:1) 使用PyAutoGUI,通过安装pip工具并执行`pip install pyautogui`安装,利用`moveRel()`方法定时移动鼠标;2) 使用Pymouse,通过`pip install pyuserinput`安装,采用`move()`方法移动鼠标绝对位置;3) 使用PyKeyboard,同样需安装pyuserinput,模拟键盘操作。文中推荐使用PyAutoGUI,因其功能丰富且文档详尽。
WK
|
8天前
|
Python
Python中format_map()方法
在Python中,`format_map()`方法用于使用字典格式化字符串。它接受一个字典作为参数,用字典中的键值对替换字符串中的占位符。此方法适用于从字典动态获取值的场景,尤其在处理大量替换值时更为清晰和方便。
WK
63 36
|
19天前
|
机器学习/深度学习 数据采集 数据挖掘
11种经典时间序列预测方法:理论、Python实现与应用
本文将总结11种经典的时间序列预测方法,并提供它们在Python中的实现示例。
56 2
11种经典时间序列预测方法:理论、Python实现与应用
|
15天前
|
开发者 Python
Python中的魔法方法与运算符重载
在Python的奇妙世界里,魔法方法(Magic Methods)和运算符重载(Operator Overloading)是两个强大的特性,它们允许开发者以更自然、更直观的方式操作对象。本文将深入探讨这些概念,并通过实例展示如何利用它们来增强代码的可读性和表达力。
|
27天前
|
Python
Python中的push方法详解与实例
Python中的push方法详解与实例
|
23天前
|
Linux Python
Python获得本机本地ip地址的方法
【10月更文挑战第8天】 socket模块包含了丰富的函数和方法,可以获取主机的ip地址,例如gethostbyname方法可以根据主机名获取ip地址,gethostbyname_ex方法可以获得本机所有ip地址列表,也可以使用netifaces模块获取网卡信息。
23 0
|
24天前
|
SQL 安全 数据库
Python防止SQL注入攻击的方法
Python防止SQL注入攻击的方法
31 0
|
24天前
|
Python
Python中tqdm模块的常用方法和示例
`tqdm` 是一个快速、可扩展的Python进度条库,适用于长循环中添加进度提示。通过封装迭代器 `tqdm(iterator)`,可以轻松实现进度显示。支持自定义描述、宽度及嵌套进度条,适用于多种迭代对象。在Jupyter notebook中,可自动调整显示效果。
26 0
|
24天前
|
Python
Python中threading模块的常用方法和示例
Python 的 `threading` 模块提供了多线程编程的能力,允许同时执行多个线程。主要类包括 `Thread`、`Lock` 和 `Condition`。`Thread` 类用于创建和管理线程,`Lock` 用于同步线程,防止资源竞争,`Condition` 用于线程间协调。本文介绍了这些类的常用方法及示例代码,帮助你更好地理解和使用多线程编程。
23 0
|
24天前
|
Shell Python
Python中os模块的常用方法和示例
在Python中,`os`模块提供了与操作系统交互的函数,用于文件和目录管理、路径操作、环境变量等。常用方法包括路径操作(如`os.path.join()`、`os.path.abspath()`)、文件和目录管理(如`os.mkdir()`、`os.remove()`)、环境变量和进程管理(如`os.getenv()`、`os.system()`)以及其他常用功能(如`os.getcwd()`、`os.urandom()`)。
24 0