系统学习Python——类(class)代码的编写基础与实例:类可以截获Python运算符

网友投稿 1137 2022-09-30

系统学习Python——类(class)代码的编写基础与实例:类可以截获Python运算符

系统学习Python——类(class)代码的编写基础与实例:类可以截获Python运算符

分类目录:​​《系统学习Python》总目录​​

现在,让我们来看类和模块的第三个也是最后一个主要差别:运算符重载。简而言之,运算符重载就是让用类编写的对象,可截获并响应用在内置类型上的运算:加法、切片、打印和点号运算等。这其实只是一种自动分发机制:表达式和其他内置运算被路由到了类内部的实现。在这点上类和模块也基本不同:模块可以实现函数调用,但却不能实现表达式的行为。

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

以双下划线命名的方法​​__X__​​是特殊钩子:在Python类中我们实现运算符重载是通过提供特殊命名的方法来拦截运算。Python语言在每种运算和特殊命名的方法之间,定义了固定不变的映射关系。当实例出现在内置运算中时,这类方法会自动被调用:例如,如果实例对象继承了一个​​__add__​​​方法,那么当对象出现在​​+​​表达式内时,该方法就会被调用。而该方法的返回值将作为相应表达式的结果。类可以重载绝大多数内置类型运算:Python中有几十种特殊运算符重载的方法的名称,几乎可截获并实现内置类型的所有运算。它不仅包括了表达式,同时还包括像打印和对象创建这样的基础运算。默认的运算符重载方法既不存在,也不需要:如果类没有定义或继承运算符重载法,那么类的实例将不能支持相应的运算。例如,如果没有​​__add__​​​,​​+​​表达式就会引发异常。新式类有一些默认的运算符重载方法,但是不属于常见运算:在Python所谓新式类中,一个名为​​object​​​的根类确实提供了某些默认的​​__X__​​方法。但是提供的不多,同时也不属于大多数常见的运算运算符将类与Python的对象模型结合到一起:通过重载类型运算,我们可以让采用类实现的用户定义对象获得与内置对象一样的行为,因此这保证了与预期接口的一致性和兼容性。

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

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

下面是另一个例子,我们要定义《​​类(class)代码的编写基础与实例:类通过继承进行定制​​​》的​​SecondClass​​的子类,实现Python会自动调用的三个特殊名称属性:

​​__init__​​​会在创建新的实例时被调用:​​self​​​是新的​​ThirdClass​​对象​​__add__​​​会在​​ThirdClass​​​实例出现在​​+​​表达式中时被调用。​​__str__​​​会在打印一个对象的时候。从技术上讲,当通过​​__str__​​内置函数或者其Python内部的等价形式来将对象转换为打印字符串的时候被调用。

我们先定义​​ThirdClass​​类:

class ThirdClass(SecondClass): def __init__(self, value): self.data = value def __add__(self, other): return ThirdClass(self.data + other) def __str__(self): return 'ThirdClass: %s' % self.data def mul(self, other): self.data *= other

然后初始化一个类实例对象并执行一些操作:

a = ThirdClass('hy592070616')a.display()print(a)

输出:

Blog="hy592070616"ThirdClass: hy592070616

然后通过重载的内置方法​​__add__​​​实现​​+​​的运算:

b = a + ' MachineLearning'b.display()print(b)

输出:

Blog="hy592070616 MachineLearning"ThirdClass: hy592070616 MachineLearning

测试​​ThirdClass​​类的其它方法:

a.mul(3)print(a)

输出:

ThirdClass: hy592070616hy592070616hy592070616

​​ThirdClass​​​是一个​​SecondClass​​​对象,所以其实例会继承《​​类(class)代码的编写基础与实例:类通过继承进行定制​​​》的​​SecondClass​​​的​​display​​​方法。但是,​​ThirdClass​​​生成的调用现在会传人一个参数(例如:​​abc​​​),这是传给​​__init__​​​构造函数内的参数​​value​​​的,并在构造函数中被赋值给​​self.data​​​。最终的效果是​​ThirdClass​​​会在创建时自动设置​​data​​​属性,而不再是必须在构建之后通过​​setdata​​调用。

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

​​__init__​​​,​​__add__​​​和​​__str__​​​这样的特殊命名方法会由子类和实例继承,就像一个​​class​​语句中被赋值的其他名称。如果这些名称没有被编写在类中,那么Python就会在该类的所有父类中寻找这类变量名。运算符重载方法的名称并不是内置变量或保留字,它们只是当对象出现在不同的上下文时Python会自动去搜索的属性。Python通常会自动进行调用,但偶尔也能被你所编写的程序代码调用。

是否返回结果

一些像​​__str__​​​的运算符重载方法要求结果,但另一些则更加灵活。例如,注意,​​__add__​​​方法是如何通过结果值调用​​ThirdClass​​​,从而创建并返回一个该类新的实例对象的。也就是说,这反过来会触发​​__add__​​​将结果初始化。这是一个常见的约定,也解释了为什么程序清单中的​​b​​​有一个​​display​​​方法;因为在该类的对象上调用​​+​​​会返回一个新的该类对象,所以​​b​​​也是一个​​ThirdClass​​对象。这本质上传播了该类型。

相比之下,​​mul​​​会在原位置修改当前的实例对象(通过重新赋值​​self​​​属性)。我们可以通过重载​​*​​​表达式来实现​​mul​​​,但这将与内置的数字与字符串的行为极其不同,因为这里​​ThirdClass​​​对象的​​*​​运算符总是创建新对象。实践证明,重载的运算符应该以与内置的运算符实现同样的方式工作。因为运算符重载其实只是一种表达式对方法的分发机制,所以你可以在自己的类对象中以任何喜欢的方式解释运算符。

为什么要使用运算符重载

作为一名类的设计者,你可以选择是否要使用运算符重载。你的决定取决于有多想让对象的用法和外观看起来更像内置类型。就像前面提到的那样,如果省略运算符重载方法而且不从父类中继承该方法,那么实例就不支持相应的运算:如果试着使用这个实例进行运算,就会引发异常(或者在一些类似打印的情形下,使用标准的默认运算符重载方法)。坦率地讲,只有在实现具有数学本质的对象时,才会用到许多运算符重载方法。例如,向量或矩阵类可以重载加法运算符,但员工类可能就不用。就较简单的类而言,可能根本不会用到重载,因此应该利用显式的方法调用来实现对象的行为。

另外,如果需要把用户定义的对象传入预期接受并使用内置类型(例如列表或字典)可用的运算符的函数,那么你可能就会决定使用运算符重载。在类中实现同一组运算符,可以保证对象能支持相同的预期的对象接口,因此会与这个函数兼容。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:关于Mybatis与JPA的优缺点说明
下一篇:小程序request请求怎么解决session失败问题(微信小程序request怎么办)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~