python 运算符重载_Python3面向对象-运算符重载

1:运算符重载介绍

运算符重载,就是在某个类的方法中,拦截其内置的操作(比如:+,-,*,/,比较,属性访问,等等),使其实例的行为接近内置类型。

当类的实例出现在内置操作中时(比如:两个实例相加 +),Python会自动调用你的方法(比如:你重载的__add__方法),并且你的方法的返回值会作为相应操作的结果。

Python3中的运算符重载:
运算符重载让类拦截常规的Python操作。
类可以重载所有Python表达式运算符。
类也可以重载打印,函数调用,属性访问等内置运算。
重载是通过在一个类中提供特殊名称的方法来实现的。

2:Python3中常见运算符重载方法

1c5d00eea9edb8045e33ae6562ba3fe7.png
8ab6f23c8e09d860765714fd65e670eb.png
6fed7a5eda17219746c0a387bb7cac05.png

3:运算符重载方法示例

3.1:索引和分片:__getitem__和__setitem__

在实例进行 类似 X[2] 这种操作时会调用__getitem__方法;
在实例进行 类似 X[2] = value 这种操作时会调用__setitem__方法;索引:

# encoding=gbk class Test:    def __getitem__(self, item):        print('item:',item)        return item**3  # 返回 x 的三次方     def __setitem__(self, key, value):        print(key,value) t = Test()print(t[2])  # 会调用__getitem__函数,   返回2 的三次方print(t[3])  # 会调用__getitem__函数,   返回3 的三次方 t[3] = 100 # 会调用__setitem__

分片:

# encoding=gbk class Test:    def __getitem__(self, item):        print('item:',item)        if isinstance(item,int):            return item**3  # 返回 x 的三次方        else:            print('slicing',item.start,item.stop,item.step)            return [x**3 for x in range(item.start,item.stop,item.step)]      def __setitem__(self, key, value):        print(key,value)        # do something t = Test()# 索引:print(t[2])  # 会调用__getitem__函数,   返回2 的三次方print(t[3])  # 会调用__getitem__函数,   返回3 的三次方t[3] = 100 # 会调用__setitem__ print('*'*60)# 分片:print(t[2:10:2]) # 传入的是分片对象t[2:5] = 100

3.2:返回数值:__index__ (__index__不是索引)

在需要整型数字的上下文中,会调用__index__函数,__index__会为实例返回一个整数值。比如:调用函数hex(X),bin(X)时,会去调用X的__index__方法:

# encoding=gbk class Test:    def __index__(self):        return 100 X = Test()print(hex(X))print(bin(X))print(oct(X))

3.3:可迭代对象:__iter__,__next__

如果要使自己定义的类的对象是可迭代的,那么就必须使这个类支持迭代协议,即重载__iter__,__next__方法。

迭代协议:(包括两种对象)

可迭代对象(Iterable):里面包含了__iter__(); 可迭代对象X, 通过调用 I = iter(X) 可返回一个迭代器对象,再调用next(I) 就可以迭代X中的元素。

迭代器对象(Iterator):里面包含了__iter__() 和 __next__()

迭代过程:(for循环,等迭代中默认的操作)

首先调用 iter函数: I = iter(X); 调用的是X.__iter__()

然后对返回对象I调用next:next(I); 调用的是 I.__next__(),直到迭代完成。

3.3.1:单遍迭代

即只能迭代一次:

# encoding=gbk class Fibonacci:    def __init__(self, n):        self.a = 0        self.b = 1        self.max_cnt = n     def __iter__(self):        return self     def __next__(self):        self.a, self.b = self.b, self.a + self.b        if self.a > self.max_cnt:            raise StopIteration        return self.a   fib = Fibonacci(5)print(fib) print('1:'+'*' * 30)I = iter(fib)  # 调用 fib.__iter__(),(返回的是self,及fib)print(next(I))  # 调用的是 fib.__next__(),fib对象中的a,b属性值会改变。print(next(I))  # 调用的是 fib.__next__() print('1:'+'*' * 30) # for循环:首先调用的是I = iter(fib),此处返回的是self即fib,再调用next(I),即fib.__next__(),此时其值已经取完2个了,因此从第3个开始取。for x in fib:    print(x)print('2:'+'*' * 30)# 此处与上面的循环一样,但是fib.__next__()已经把数据取完了,故这里不会有输出!for x in fib:    print(x)  print('3:'+'*' * 30) #i = iter(fib)  # 返回self,即fibfor ii in i:  #  与上面的 for x in fib 一样,不会再输出!    print(ii)

3.3.2:多遍迭代

即可以多次迭代使用:

# encoding=gbk class Fibonacci:    def __init__(self, n):        self.a = 0        self.b = 1        self.max_cnt = n     def __iter__(self):        return FibonacciIter(self.a,self.b,self.max_cnt) class FibonacciIter:    def __init__(self,a,b,max_cnt):        self.a = a        self.b = b        self.max_cnt = max_cnt     def __next__(self):        self.a, self.b = self.b, self.a + self.b        if self.a > self.max_cnt:            raise StopIteration        return self.a fib = Fibonacci(5) I = iter(fib)print(next(I))  # 调用的是FibonacciIter对象中的__next__方法,fib对象中的a,b属性没有任何变化。print(next(I))print(next(I))print(next(I))print(next(I))  # 迭代完了# print(next(I))   # 上一步迭代完了,再次调用next(I)会抛出异常, print('1:'+'*' * 30) print(fib)print('1:'+'*' * 30)# for循环(默认调用):首先调用一次 I = iter(fib) 即fib.__iter__(),返回一个重新创建FibonacciIter对象, ;#                      然后再调用 next(I),即I.__next__() (也就是FibonacciIter类中的__next__()函数),直到迭代完。for x in fib:    print(x)print('2:'+'*' * 30)# 与上面for循环调用过程一样。for x in fib:    print(x) print('3:'+'*' * 30) # i = iter(fib)# for ii in i:  # 这里会报错,因为iter(fib)返回FibonacciIter的实例 i ,                # 而for循环首先会调用 iter(i) 即 调用FibonacciIter中的__iter__函数,而FibonacciIter类中没有重载此函数;#     print(ii)

3.3.3:__iter__ 加 yield 实现多遍迭代

# encoding=gbk class Test:    def __init__(self,start,stop):        self.start = start        self.stop = stop     def __iter__(self):        print(self.start,self.stop+1)        for value in range(self.start,self.stop+1):            yield value**2 t = Test(1,3)# 说明:for循环中,首先调用 I = iter(t),即调用的是t.__iter__(),在__iter__函数中有yield语句,#      yield语句会自动创建一个包含 __next__ 方法的类,并返回它的实例,#      然后会调用 next(I),I 为yield自动创建类的实例for i in t:    print(i)print('*'*40)for i in t:    print(i)

3.4:属性访问:__getattr__,__getattribute__和__setattr__

3.4.1 __getattr__,__getattribute__

__getattr__ 会拦截未定义的属性,即在使用点号访问属性时(如:X.属性) ,如果Python通过其继承树搜索过程中没有找到这个属性,那么就会自动调用__getattr__方法。

__getattribute__ 会拦截所有属性。

# encoding=gbk class Test:    aa = 0    def __init__(self):        self.age = 100    def __getattr__(self, item):        print('in __getattr__:',item)   t = Test()print('1:' ,t.__dict__)# 属性引用,属性找不到时,就会调用__getattr__方法print(t.aa)  # 在类中存在类属性 aa,print('2:'+ '*' * 30) print(t.bb)  # t.__dict__ 中不存在属性 bb,其父类中也没有属性bb,故会调用__getattr__方法print('3:'+ '*' * 30)print(t.age)  #  存在实例属性age,不会调用__getattr__方法t.age = 200print('4:' ,t.__dict__) """1: {'age': 100}02:******************************in __getattr__: bbNone3:******************************1004: {'age': 200}"""

3.4.2__setattr__

__setattr__:会拦截所有的属性赋值

如果定义或者继承了__setattr__方法,那么 self.attr = value,将会变成 self.__setattr__('attr',value)

这里要注意的是 如果在__setattr__方法中有使用 self.attr = value 的赋值形式,那么__setattr__将会进入死循环,因为self.attr = value 的赋值形式会调用self.__setattr__('attr',value),而__setattr__方法中又使用self.attr = value进行赋值,从而进入一个循环。

# encoding=gbk class Test:     def __init__(self):        #  构造函数中对 self.age  进行赋值,如果继承了__setattr__方法,        #  就会把self.age = 100   变成 self.__setattr__('age',100)        self.age = 100    def __getattr__(self, item):        print('in __getattr__:',item)     def __setattr__(self, key, value):        print('in __setattr__:',key,value)        # self.aa = 100  # 这样赋值会导致死循环,因为 self.aa = 100  会变成 self.__setattr__('aa',100),而后者又调用了前者        if key != 'age':  # 拦截 age属性            self.__dict__[key] = value   t = Test()print('1:' + '*'*30)print(t.__dict__)   #  此处输出为{};虽然在构造函数中有self.age = 100赋值,但是在 __setattr__方法中把它过滤掉啦print('2:' + '*'*30)print(t.age)  # 由于 age属性被拦截掉了,故访问t.age会调用 __getattr__方法print('3:' + '*'*30)t.age = 200 # 会把 age给拦截掉t.name = 'ixusy' #  不存在name属性,因此会调用__setattr__,在__setattr__方法中把 name属性 添加到属性字典__dict__中,                  #  后面就可以通过使用t.name进行访问。print('4:' + '*'*30)print(t.__dict__)print('5:' + '*'*30)  """输出结果:in __setattr__: age 1001:******************************{}2:******************************in __getattr__: ageNone3:******************************in __setattr__: age 200in __setattr__: name ixusy4:******************************{'name': 'ixusy'}5:******************************"""

3.4.3 __getattr__ 和 __setattr__总结

__getattr__ :拦截不存在的属性引用!

__setattr__:拦截所有的属性赋值,当心死循环!

3.5:调用表达式:__call__

在实例上执行函数调用表达式,就会自动调用__call__函数

# encoding=gbk class Test:    def __call__(self, *args, **kwargs):        print('call:',args,kwargs) t = Test()t(1,2,3)t(1,2,3,b=22)# 传递参数,需要符合函数传递参数的规则# t(1,a=2,3,b=22)  # 这样传递会报错

3.6:字符串显示:__str__ 和 __repr__

__str__ 和 __repr__ 都是用于显示字符串,只不过是他们的使用场景不同而已。

__str__ :打印操作(print),内置函数str调用,会优先调用__str__ ,如果没有重载__str__,就会去调用__repr__;

__repr__:用于所有其他场景:包括交互式命令行,repr函数,嵌套显示,以及没有可用__str__时,print和str的调用。

__repr__ 可用于任何地方,__str__用于print 和 str函数。

3.7:比较运算

# encoding=gbk class Person:    def __init__(self,name,age,height):        self.name = name        self.age = age        self.height = height     #  比较规则可以自行定义,    # 下面规则为:    # 1:年龄小的 比较结果为小    # 2:年龄相等的,比较身高:身高小,结果为小    # 3:其他情况返回False    def __lt__(self, other):        if self.age < other.age:            return  True        elif self.age ==  other.age:            return  self.height < other.height        else:            return False     """    还可以重载:        __gt__        __le__        __ge__        __eq__        __ne__                需要注意的是 p1 == p2,并不表示p1 != p2, 具体要看你怎么实现 __eq__,__ne__方法,        实际中尽可能使得__eq__,__ne__方法的实现符合正常的逻辑。    """  p1 = Person('ixusy88',18,188)p2 = Person('i',18,180)print(p1 < p2)  # False p1 = Person('ixusy88',18,177)p2 = Person('i',18,180)print(p1 < p2)  # True
e29ca1535cd4c4cead408dd09b2f20d5.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker Failed to get D-Bus connection 报错

在centos7的容器里面出现了一个BUG&#xff0c;就是serveice启动服务的时候出现报错&#xff0c;不能用service启动服务。[roote13c3d3802d0 /]# service httpd startRedirecting to /bin/systemctl start httpd.serviceFailed to get D-Bus connection: Operation not permit…

牛顿法、拟牛顿法、高斯-牛顿法、共轭梯度法推导总结

原文&#xff1a;http://ihoge.cn/2018/newton1.html 前言&#xff1a; 线性最小二乘问题&#xff0c;我们可以通过理论推导可以得到其解析解&#xff0c;但是对于非线性最小二乘问题&#xff0c;则需要依赖迭代优化的方法&#xff0c;牛顿算法是解决非线性最优的常见算法之一…

不用变量交换2个值

题目: 不使用变量交换2个值 代码如下: #include <iostream> using namespace std;int main() { int a = 3;int b = 4;cout<<"a="<<a<<endl;cout<<"b="<<b<<endl;a = a ^ b;b = a ^ b;a = a ^ b;cout<&…

基于事件驱动架构构建微服务第16部分:Azure Active Directory B2C

原文链接: https://logcorner.com/building-micro-services-through-event-driven-architecture-part16-azure-active-directory-b2c/在本教程中&#xff0c;我将展示如何通过使用Azure AD B2C启用Oauth2和OpenId Connect来保护微服务。Azure Active Directory B2C将B2C身份认证…

java 栈和队列实现迷宫代码_LeetCode每日一题--剑指 Offer 09. 用两个栈实现队列(Java)

DailyChallenge剑指 Offer 09. 用两个栈实现队列Easy20200630Description用两个栈实现一个队列。队列的声明如下&#xff0c;请实现它的两个函数 appendTail 和 deleteHead &#xff0c;分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素&#xff0c;d…

提升方法之AdaBoost算法

提升方法之AdaBoost算法 作为非数学专业出身看到密密麻麻的数学公式刚开始真的是非常头疼。算法的物理逻辑的时候尚能理解&#xff0c;但是涉及到具体的数学公式实现就开始懵逼了&#xff1a;为什么要用这个公式&#xff0c;这个公式是怎么推到的&#xff0c;这个公式达到什么…

C# 开源一个基于 yarp 的 API 网关 Demo,支持绑定 Kubernetes Service

关于 Neting刚开始的时候是打算使用微软官方的 Yarp 库&#xff0c;实现一个 API 网关。目前写完了查看 Kubernetes Service 信息、创建 Route 和 Cluster 和绑定 Kubernetes Service。简单来说&#xff0c;就是完成了基础部分&#xff0c;配置路由和后端服务绑定&#xff0c;如…

CCR源码分析-CCR架构

CCR&#xff0c;并发与协调运行时(Concurrency and Coordination Runtime)。从名字里我们就可以知道&#xff0c;这个东东是用来简化并发程序设计的。为何要并发呢&#xff1f;因为我们有多个任务需要处理&#xff0c;如果能同时做就会充分利用硬 件而减少处理的时间。自然的&a…

量子力学到底有多难?他用700多幅漫画,让孩子秒懂这个神奇世界!

▲ 点击查看很多朋友应该都看过Facebook创始人扎克伯格给他的女儿讲量子力学的那张照片。扎克伯格在清华大学经济管理学院做演讲时&#xff0c;曾谈到&#xff1a;学习量子力学改变了他的思维方式。到底什么是量子力学&#xff1f;我们生活面对的物质尺度大约是厘米级到千米级之…

Openssl搭建私有CA认证

概述CA英文全称Certification Authority&#xff0c;即数字证书认机构。从广义上来说&#xff0c;CA是负责发放和管理数字证书的权威机构&#xff0c;并作为用户数字认证中受信任的第三方&#xff0c;承担公钥体系&#xff08;PKI&#xff09;中公钥的合法性检验的责任&#xf…

提升树算法总结(一)

本文是综合了之前的以往多个笔记汇总而成&#xff0c;内容较长。感兴趣的建议收藏用到时随时翻阅内容包含&#xff1a; 若有遗漏错误之处请反馈修改&#xff0c;感激不尽 一、Boosting基本概念 二、前向分步加法模型1. 加法模型2. 前向分步算法 三、AdaBoost1. 算法解析2. 模…

Android之Launcher分析和修改4——初始化加载数据

上面一篇文章说了Launcher是如何被启动的&#xff0c;Launcher启动的过程主要是加载界面数据然后显示出来&#xff0c; 界面数据都是系统APP有关的数据&#xff0c;都是从Launcher的数据库读取&#xff0c;下面我们详细分析Launcher如何加载数据。 在Launcher.java的onCreate()…

图像处理技术之分辨率与压缩

一 图像分辨率数码图像有两大类&#xff0c;一类是矢量图&#xff0c;也叫向量图&#xff1b;另一类是点阵图&#xff0c;也叫位图。矢量图比较简单&#xff0c;它是由大量数学方程式创建的&#xff0c;其图形是由线条和填充颜色的块面构成的&#xff0c;而不是由像素组成的&am…

当代年轻人,都有些不成文的规定?

全世界只有3.14 % 的人关注了爆炸吧知识面对海量信息&#xff0c;我们不可能有时间去一一筛选&#xff0c;导致我们看到的总是局部。包括公众号&#xff0c;看似可以随便关注&#xff0c;但是你的选择其实是有限空间的。你可以关注更多资讯、干货十足的公号主动和别人分享有趣的…

未检测到正确安装的网络适配器_电脑网络适配器有感叹号怎么解决?

今天有位女性乎友问&#xff1a;我家的电脑之前一直没装宽带&#xff0c;主要是家人怕影响学习&#xff0c;不过上周就涨了&#xff0c;电脑终于连上网线&#xff0c;但没办法上网&#xff0c;还发现电脑设备管理器中网络适配器出现黄色感叹号。对于电脑的网络适配器感叹号问题…

使用Spectre.Console创建漂亮的控制台应用程序

前言你是否厌倦了控制台应用程序默认的简陋界面&#xff1f;这时&#xff0c;你可以试试引用Nuget包Spectre.Console。打印使用帮助首先&#xff0c;我们可以设置控制台应用程序可以执行的命令&#xff1a;static async Task<int> Main(string[] args) {var app new Com…

Android之ComponentName的用法

ComponentName(组件名称)是用来打开其他应用程序中的Activity或服务的、 ComponentName,顾名思义,就是组件名称,通过调用Intent中的setComponent方法,我们可以打开另外一个应用中的Activity或者服务。 实例化一个ComponentName需要两个参数,第一个参数是要启动应用的包…

Linux入门之进程管理(4)之进程与文件

Linux入门之进程管理(4)之进程与文件前面使用进程相关命令管理工具都是根据进程编号或者进程名称及其其它属性信息来查看和处理相关进程的&#xff0c;但是在某些情况下&#xff0c;想要查看某个文件或者某个设备被哪些进程所使用&#xff0c;使用ps、pgrep等命令查询的是不够准…