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 = n
def __iter__(self):
return self #实例本身就是迭代对象,故返回自己
def __next__(self):
if self.n >10:
raise StopIteration #如果超过10就报StopIteration 错误
self.n = self.n + 1
return self.n
f = Foo(7)
for i in f: #for循环自动调用__next__方法,实现了迭代取值
print(i)
例子2
输出100内的斐契那波数列
class F:
def __init__(self):
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
self.a ,self.b = self.b , self.a + self.b
if self.a > 100:
raise StopIteration
return self.a
f = 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 = name
b = 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
#描述符Str
class Str:
def __get__(self, instance, owner):
print('Str调用')
def __set__(self, instance, value):
print('Str设置...')
def __delete__(self, instance):
print('Str删除...')
#描述符Int
class 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=name
self.age=age
#何地?:定义成另外一个类的类属性
#何时?:且看下列演示
p1=People('alex',18)
#描述符Str的使用
p1.name
p1.name='egon'
del p1.name
#描述符Int的使用
p1.age
p1.age=18
del 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}
True
True
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 = name
p = 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 = name
p = People("nick") #实例化对象,调用数据描述符的__set__,
# 但是由于描述符的__set__只是执行了打印操作,什么都没做,所以p对象的__dict__什么都没有
p.name = "nicholas"
print(p.__dict__) #输出的结果为空
#因此可以得出结论,数据描述符的优先级大于实例属性(字典操作)
例子3
class Foo(object):
def __init__(self):
pass
def __get__(self, instance, owner):
print("执行了__get__")
class People(object):
name = Foo("x")
def __init__(self,name,age):
self.name = name
self.age = age
p = People("nick",18) #实例化对象,这里由于是非数据描述符,优先级低于实例属性,
# 所以这里直接设置了实例属性,而不再调用描述符
print(p.name) #打印直接输出实例属性
print(p.__dict__)
#输出的结果:{'name': 'nick', 'age': 18}
#因此可以得出结论,实例属性的优先级大于非数据描述符
例子4
class Foo(object):
def __init__(self,name2):
self.name2 = name2
def __get__(self, instance, owner):
print("执行了__get__")
class People(object):
name = Foo("x")
def __init__(self,name,age):
self.name = name
self.age = age
def __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 = key
self.expect_type = expect_type
def __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 = name
self.age = age
p1 = 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 = key
self.expect_type = expect_type
def __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 TypeError
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 = name
self.age = age
p1 = 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 = name def __enter__(self):
print("执行__enter__")
return self def __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("上下文管理协议")
最新文章
- sip协议音视频性能测试
- C语言简易文法(无左递归)
- hdu 4411 2012杭州赛区网络赛 最小费用最大流 ***
- atitit.提升开发效率---mda 软件开发方式的革命
- 根据BIOS信息修改主机名
- batch批的概念
- html之a标签
- linux中/etc/init.d [转]
- AngularJs学习笔记4——四大特性之双向数据绑定
- 漫画研发之十二:该听谁的? 部门经理 or 项目经理
- C++ STL 双端队列deque详解
- PHP初入--表单元素
- 《团队-Android手机便签-项目进度》
- 在Codeblocks下配置GoogleTest单元测试工具
- join算法分析
- Java设计模式学习记录-解释器模式
- Windows7安装两个jdk配置
- ms11_050漏洞攻击
- sql一些语句性能及开销优化
- 深入浅出 JIT 编译器