Python之路(第二十七篇) 面向对象进阶:内置方法、描述符
一、__call__
对象后面加括号,触发执行类下面的__call__
方法。
创建对象时,对象 = 类名() ;而对于 __call__
方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:def __call__(self, *args, **kwargs):print("我执行啦")f = Foo()f() #对象加括号调用执行类下的__call__方法#输出结果 我执行啦
二、__next__
和__iter__
实现迭代器协议
迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)
可迭代对象执行obj.__iter__()
得到的结果就是迭代器对象。
在类中,如果有__iter__
和__next__
内置方法,那么就构成了迭代器。
例子
class Foo:def __init__(self,n):self.n = ndef __iter__(self):return self #实例本身就是迭代对象,故返回自己def __next__(self):if self.n >10:raise StopIteration #如果超过10就报StopIteration 错误self.n = self.n + 1return self.nf = Foo(7)for i in f: #for循环自动调用__next__方法,实现了迭代取值print(i)
例子2
输出100内的斐契那波数列
class F:def __init__(self):self.a = 0self.b = 1def __iter__(self):return selfdef __next__(self):self.a ,self.b = self.b , self.a + self.bif self.a > 100:raise StopIterationreturn self.af = F()for i in f:print(i)
三、描述符(__get__,__set__,__delete__
)
描述符(descriptor):
1、描述符本质
就是一个新式类,在这个新式类中,至少实现了__get__()
,__set__()
,__delete__()
中的一个,这也被称为描述符协议。__get__()
:调用一个属性时,触发__set__()
:为一个属性赋值时,触发__delete__()
:采用del删除属性时,触发
2、描述符的作用
是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
描述符是在另外一个类的类属性进行定义的,描述符在一个类的类属性__dict__
字典里
例子1
class Foo:def __get__(self, instance, owner):print("执行了__get__")def __set__(self, instance, value):print("执行了__set__")def __delete__(self, instance):print("执行了__delete__")class Bar:x = Foo()def __init__(self,name):self.name = nameb = Bar("nick")b.x #调用执行描述符里的__get__方法print(b.x) #b.x = 1 # 调用执行描述符里的__set__方法print(b.__dict__)del b.x #调用执行描述符里的__delete__方法print(b.__dict__)
输出结果
执行了__get__执行了__get__None执行了__set__{'name': 'nick'}执行了__delete__{'name': 'nick'}
例子2
#描述符Strclass Str:def __get__(self, instance, owner):print('Str调用')def __set__(self, instance, value):print('Str设置...')def __delete__(self, instance):print('Str删除...')#描述符Intclass Int:def __get__(self, instance, owner):print('Int调用')def __set__(self, instance, value):print('Int设置...')def __delete__(self, instance):print('Int删除...')class People:name=Str()age=Int()def __init__(self,name,age): #name被Str类代理,age被Int类代理,self.name=nameself.age=age#何地?:定义成另外一个类的类属性#何时?:且看下列演示p1=People('alex',18)#描述符Str的使用p1.namep1.name='egon'del p1.name#描述符Int的使用p1.agep1.age=18del p1.age#我们来瞅瞅到底发生了什么print("__p1.__dict__",p1.__dict__)print(People.__dict__)#补充print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的print(type(p1).__dict__ == People.__dict__)
输出结果
Str设置...Int设置...Str调用Str设置...Str删除...Int调用Int设置...Int删除...__p1.__dict__ {}{'__module__': '__main__', 'name': <__main__.Str object at 0x021C6850>, 'age': <__main__.Int object at 0x021C6870>, '__init__': <function People.__init__ at 0x021C5DB0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}TrueTrue
3、描述符分两种
(1) 数据描述符:至少实现了__get__()
和__set__()
class Foo:def __set__(self, instance, value):print('set')def __get__(self, instance, owner):print('get')
(2) 非数据描述符:没有实现__set__()
class Foo:def __get__(self, instance, owner):print('get')
注意:非数据描述符一般是只有__get__,如果保留__delete__执行会报错。
4、 注意事项:
(1)描述符本身应该定义成新式类,被代理的类也应该是新式类(python3中全部是新式类)
(2)必须把描述符定义成另外一个类的类属性,不能为定义到构造函数中,
(3)要严格遵循该优先级,优先级由高到底分别是
a.类属性b.数据描述符c.实例属性d.非数据描述符e.找不到的属性触发__getattr__()
例子1
class Foo:def __get__(self, instance, owner):print("执行了__get__")def __set__(self, instance, value):print("执行了__set__")def __delete__(self, instance):print("执行了__delete__")class People:name = Foo()def __init__(self,name):self.name = namep = People("nick")People.name = "nick" #调用执行了描述符的__set__方法,这一步类属性由之前的描述符被定义成另外一个字符串,# 所以下面再次调用就无法再次使用描述符了People.name #可以得出结论,类属性的优先级大于数据描述符
例子2
class Foo:def __get__(self, instance, owner):print("执行了__get__")def __set__(self, instance, value):print("执行了__set__")def __delete__(self, instance):print("执行了__delete__")class People:name = Foo()def __init__(self,name):self.name = namep = People("nick") #实例化对象,调用数据描述符的__set__,# 但是由于描述符的__set__只是执行了打印操作,什么都没做,所以p对象的__dict__什么都没有p.name = "nicholas"print(p.__dict__) #输出的结果为空#因此可以得出结论,数据描述符的优先级大于实例属性(字典操作)
例子3
class Foo(object):def __init__(self):passdef __get__(self, instance, owner):print("执行了__get__")class People(object):name = Foo("x")def __init__(self,name,age):self.name = nameself.age = agep = People("nick",18) #实例化对象,这里由于是非数据描述符,优先级低于实例属性,# 所以这里直接设置了实例属性,而不再调用描述符print(p.name) #打印直接输出实例属性print(p.__dict__)#输出的结果:{'name': 'nick', 'age': 18}#因此可以得出结论,实例属性的优先级大于非数据描述符
例子4
class Foo(object):def __init__(self,name2):self.name2 = name2def __get__(self, instance, owner):print("执行了__get__")class People(object):name = Foo("x")def __init__(self,name,age):self.name = nameself.age = agedef __getattr__(self, item):print("__getattr__")p = People("nick",18) #实例化对象,这里由于是非数据描述符,优先级低于实例属性,# 所以这里直接设置了实例属性,而不再调用描述符print(p.name)print(p.sex) #调用不存在的属性执行了__getattr__print(p.__dict__)#输出的结果:{'name': 'nick', 'age': 18}
5、描述符的应用
例子1
class Type:def __init__(self,key,expect_type):self.key = keyself.expect_type = expect_typedef __get__(self, instance, owner):print("执行__get__方法")print(self) #这里的self就是type类的对象print(instance) #这里的instance就是传入的People类的对象print("执行__get__方法")return instance.__dict__[self.key] #通过instance的字典获取对象的属性值def __set__(self, instance, value):print("执行__set__方法")instance.__dict__[self.key] = value #instance是另一个类的对象,这里要设置对象的属性字典def __delete__(self, instance):print("执行__delete__方法")instance.__dict__.pop(self.key) #删除对象的属性class People:name = Type("name",str)age = Type("age",int)def __init__(self,name,age):self.name = nameself.age = agep1 = People("nick",18) #调用2次描述符,对对象的字典进行设置print(p1.name) #通过数据描述符获取对象的属性值print(p1.__dict__)p1.age = 20 #调用描述符对对象进行设置print(p1.__dict__)
输出结果
执行__set__方法执行__set__方法执行__get__方法<__main__.Type object at 0x004CB4F0><__main__.People object at 0x02106DF0>执行__get__方法nick{'name': 'nick', 'age': 18}执行__set__方法{'name': 'nick', 'age': 20}
例子2
class Type:def __init__(self,key,expect_type):self.key = keyself.expect_type = expect_typedef __get__(self, instance, owner):print("执行__get__方法")print(self) #这里的self就是type类的对象print(instance) #这里的instance就是传入的People类的对象print("执行__get__方法")return instance.__dict__[self.key] #通过instance的字典获取对象的属性值def __set__(self, instance, value):print("执行__set__方法")if not isinstance(value,self.expect_type):print("您输入的%s不是%s"%(self.key,self.expect_type))raise TypeErrorinstance.__dict__[self.key] = value #instance是另一个类的对象,这里要设置对象的属性字典def __delete__(self, instance):print("执行__delete__方法")instance.__dict__.pop(self.key) #删除对象的属性class People:name = Type("name",str)age = Type("age",int)def __init__(self,name,age):self.name = nameself.age = agep1 = People("nick",18) #调用2次描述符,对对象的字典进行设置print(p1.name) #通过数据描述符获取对象的属性值print(p1.__dict__)p1.age = 20 #调用描述符对对象进行设置print(p1.__dict__)# p1.name = 11 #通过描述符的if not isinstance(value,self.expect_type)判断属性的类型# p2 = People(88,18) #通过描述符的if not isinstance(value,self.expect_type)判断属性的类型
四、__enter__
和__exit__
打开文件操作用 with open() as f操作,这叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__
和__exit__
方法。
__enter__(self)
:当with开始运行的时候触发此方法的运行
__exit__(self, exc_type, exc_val, exc_tb)
:当with运行结束之后触发此方法的运行
exc_type如果抛出异常,这里获取异常的类型
exc_val如果抛出异常,这里显示异常内容
exc_tb如果抛出异常,这里显示所在位置
用途或者说好处:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__
中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
例子
class OPEN:def __init__(self,name):self.name = namedef __enter__(self):print("执行__enter__")return selfdef __exit__(self, exc_type, exc_val, exc_tb):print("执行__exit__")print(exc_type)print(exc_val)print(exc_tb)print("执行__exit__2222")with OPEN("a.txt") as f:print(f) #执行打印__enter__内置方法,同时打印内置方法返回的结果#with 语句结束时执行__exit__方法,没有错误则打印None,有错误则打印错误的信息
print("上下文管理协议")
转载于:https://www.cnblogs.com/Nicholas0707/p/9458021.html
Python之路(第二十七篇) 面向对象进阶:内置方法、描述符相关推荐
- Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法
一.静态属性 静态属性相当于数据属性. 用@property语法糖装饰器将类的函数属性变成可以不用加括号直接的类似数据属性. 可以封装逻辑,让用户感觉是在调用一个普通的数据属性. 例子 class R ...
- 面向对象进阶------内置函数 str repr new call 方法
__new__方法: 我们来讲个非常非常重要的内置函数和init一样重要__new__其实在实例话对象的开始 是先继承父类中的new方法再执行init的 就好比你生孩子 先要把孩子生出来才能对孩子 ...
- Python入门学习 DAY 05 字符串,列表的内置方法
Python入门 DAY 05 本篇文章主要介绍字符串类型及内置方法,列表类型及内置方法 1.字符串类型及内置方法 (1)字符串定义 1.字符串中的字符是有序的 可以通过 ...
- Python之路(第十七篇)logging模块
一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变 ...
- Python之路(第二十篇) subprocess模块
一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念 ...
- Python之路(第二十一篇) re模块
一.re模块 正则表达式本身是一种小型的.高度专业化的编程语言,正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re,正则表达式模式被编译成一系列的字节码,然 ...
- python基础30个常用代码大全-Python3列表内置方法大全及示例代码小结
Python中的列表是简直可说是有容乃大,虽然看似类似C中的数组,但是Python列表可以接受任意的对象元素,比如,字符串,数字,布尔值,甚至列表,字典等等,自由度提升到一个新的高度,而Python也 ...
- python全栈开发基础【第十七篇】面向对象反射和内置方法
一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被 ...
- Python之路【第一篇】:Python简介和入门
Python之路[第一篇]:Python简介和入门 Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗 ...
最新文章
- odoo开发笔记--一个模块显示两个一级菜单
- MinGW+Lua环境配置小结
- MySQL复习资料(六)——MySQL-多表联合查询
- Visio工具 UML的建模
- pbs 写matlab作业,pbs提交作业
- 加息造成的定期存款转存临界点!
- 天 月_财务结算专业术语快来学习!月结和月结30天的区别
- linux java 错误日志_求大神解决啊啊 啊,看看这个怎么了,在LINUX运行完的错误日志...
- 利尔达蓝牙模组LSD4BT-E66ALSP001使用透传与手机APP(LightBlue)通讯测试
- 并查集算法题-We Are A Team-python实现
- RGB和CMYK配色表
- 打造自己的博客(一)利用VuePress完成博客整体搭建,并支持评论和自动发布
- 【产业互联网周报】房企分拆物业公司上市潮来临,“智慧社区”加码;旷视智慧物流业务定位及战略布局;京东健康与药明康德入股卫宁科技...
- gcc10环境下bwa安装报错的解决方案
- Postgresql 配置文件详解
- 如何给自己的手机录制屏幕视频
- Luogu 1880 合并石子
- 服务器ssh升级导致的java程序ssh连接失败
- 发射功率 dBm 计算
- keras提取网络中间层输出、中间层特征