返回介绍

类可以截获 Python 运算符

发布于 2024-01-29 22:24:15 字数 2807 浏览 0 评论 0 收藏 0

现在,让我们来看类和模块的第三个主要差别:运算符重载。简而言之,运算符重载就是让用类写成的对象,可截获并响应用在内置类型上的运算:加法、切片、打印和点号运算等。这只是自动分发机制:表达式和其他内置运算流程要经过类的实现来控制。这里也和模块没有什么相似之处:模块可以实现函数调用,而不是表达式的行为。

虽然我们可以把所有类行为实现为方法函数,运算符重载则让对象和Python的对象模型更紧密地结合起来。此外,因为运算符重载,让我们自己的对象行为就像内置对象那样,这可促进对象接口更为一致并更易于学习,而且可让类对象由预期的内置类型接口的代码处理。以下是重载运算符主要概念的概要。

·以双下划线命名的方法(__X__)是特殊钩子。Python运算符重载的实现是提供特殊命名的方法来拦截运算。Python语言替每种运算和特殊命名的方法之间,定义了固定不变的映射关系。

·当实例出现在内置运算时,这类方法会自动调用。例如,如果实例对象继承了__add__方法,当对象出现在+表达式内时,该方法就会调用。该方法的返回值会变成相应表达式的结果。

·类可覆盖多数内置类型运算。有几十种特殊运算符重载的方法的名称,几乎可截获并实现内置类型的所有运算。它不仅包括了表达式,而且像打印和对象建立这类基本运算也包括在内。

·运算符覆盖方法没有默认值,而且也不需要。如果类没有定义或继承运算符重载方法,就是说相应的运算在类实例中并不支持。例如,如果没有__add__,+表达式就会引发异常。

·运算符可让类与Python的对象模型相集成。重载类型运算时,以类实现的用户定义对象的行为就会像内置对象一样,因此,提供了一致性,以及与预期接口的兼容性。

运算符重载是可选的功能。主要是替其他Python程序员开发工具的人在使用它,而不是那些应用程序开发人员在使用。此外,不客气地讲,不要因为这看起来很“酷”就随便去试用。除非类需要模仿内置类型接口,不然应该使用更简单的命名方法。例如,员工数据库应用程序为什么要支持像*和+这类表达式呢?通常来说,像giveRaise和promote这类名称的方法更有意义。

因此,我们不会在本书中深入讨论Python每个可用的运算符重载方法。不过,有个运算符重载方法,你可能会在每个现实的Python类中遇见:__init__方法,也称为构造函数方法,它是用于初始化对象的状态的。你应该特别注意这个方法,因为__init__和self参数是了解Python的OOP程序代码的关键之一。

第三个例子

这是另一个例子。这一次,我们要定义Second Class的子类,实现三个特殊名称的属性,让Python自动进行调用:

·当新的实例构造时,会调用__init__(self是新的ThirdClass对象)[1]

·当ThirdClass实例出现在+表达式中时,则会调用__add__。

·当打印一个对象的时候(从技术上讲,当通过str内置函数或者其Python内部的等价形式来将其转换为打印字符串的时候),运行__str__。

新的子类也定义了一个常规命名的方法,叫做mul,它在原处修改该实例的对象。如下是一个新的子类:

ThirdClass“是一个”SecondClass对象,所以其实例会继承SecondClass的display方法。但是,Third Class生成的调用现在会传递一个参数(例如,"abc"),这是传给__init__构造函数内的参数value的,并将其赋值给self.data。直接效果是,ThirdClass计划在构建时自动设置data属性,而不是在构建之后请求setdata调用。

此外,ThirdClass对象现在可以出现在+表达式和print调用中。对于+,Python把左侧的实例对象传给__add__中的self参数,而把右边的值传给other,如图26-3所示;__add__返回的内容成为+表达式的结果。对于print,Python把要打印的对象传递给__str__中的self;该方法返回的字符串看做是对象的打印字符串。使用__str__,我们可以用一个常规的print来显示该类的对象,而不是调用特殊的display方法。

图 26-3 在运算符重载中,在类的实例上执行的表达式运算符,和其他内置运算都会对应到类中特殊名称的方法。这些特殊方法是选用的,也可像平常那样继承。在这里,“+”表达式会触发"__add__"方法

__init__、__add__和__str__这样的特殊命名的方法会由子类和实例继承,就像这个类中赋值的其他变量名。如果方法没有在类中编写,Python会在其所有超类内寻找这类变量名。运算符重载方法的名称并不是内置变量或保留字,只是当对象出现在不同的环境时Python会去搜索的属性。Python通常会自动进行调用,但偶尔也能由程序代码调用(稍后会谈到这个问题。例如,__init__通常可手动调用来触发超类的构造函数)。

注意,__add__方法创建并返回这个类的新的实例对象(通过它的结果值调用ThirdClass)。但是,mul会在原处修改当前的实例对象(通过重新赋值self属性)。我们可以通过重载*表达式来实现后者,但是,这一点和内置类型的行为不同,就像数字和字符串,总是替*运算符创建新对象。通常的实践说明,重载的运算符应该以与内置的运算符实现同样的方式工作。因为运算符重载其实只是表达式对方法的分发机制,可以在自己的类对象中以任何喜欢的方式解释运算符。

[1]不要与模块色中的__init__.py文件相混淆!见第23章,以获取更详细的信息。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文