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("上下文管理协议")
Python之路(第二十七篇) 面向对象进阶:内置方法、描述符的更多相关文章
- Python之路(第二十三篇) 面向对象初级:静态属性、静态方法、类方法
一.静态属性 静态属性相当于数据属性. 用@property语法糖装饰器将类的函数属性变成可以不用加括号直接的类似数据属性. 可以封装逻辑,让用户感觉是在调用一个普通的数据属性. 例子 class R ...
- python 面向对象进阶之内置方法
一 isinstance(obj,cls)和issubclass(sub,super) 1.1,isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(obj ...
- python成长之路第三篇(3)_内置函数及生成器迭代器
打个广告欢迎加入linux,python资源分享群群号:478616847 目录: 1.lambda表达式 2.map内置函数 3.filter内置函数 4.reduce内置函数 5.yield生成器 ...
- 面向对象进阶------>内置函数 str repr new call 方法
__new__方法: 我们来讲个非常非常重要的内置函数和init一样重要__new__其实在实例话对象的开始 是先继承父类中的new方法再执行init的 就好比你生孩子 先要把孩子生出来才能对孩子 ...
- Python—面向对象06 内置方法
一 .isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object ...
- python day21 ——面向对像-反射 getattr,内置方法
一.反射:用字符串数据类型的变量名来访问这个变量的值 上代码^_^ # class Student: # ROLE = 'STUDENT' # @classmethod # def check_cou ...
- Python之路(第十七篇)logging模块
一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变 ...
- Python之路(第二十篇) subprocess模块
一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念 ...
- python3-面向对象进阶(内置方法)
面向对象进阶: isinstance和issubclass 反射 __setattr__,__getattr,__delattr__ __setitem__,__getitem,__delitem__ ...
随机推荐
- HTTP协议(Requset、Response)
目录 http协议 http报文解析: Http请求(浏览器->服务器) HttpServletRequest对象: Http响应(服务器->浏览器) HttpServletRespons ...
- Spring集成MyBatis的使用-使用SqlSessionTemplate
Spring集成MyBatis的使用 Spring集成MyBatis,早期是使用SqlSessionTemplate,当时并没有用Mapper映射器,既然是早期,当然跟使用Mapper映射器是存在一些 ...
- JSF web.xml的各类参数属性配置
出处:http://www.cnblogs.com/zxpgo/articles/2570175.html 感谢作者的分享!! ———————————————————————————————————— ...
- 修改mysql 数据库编码
查看编码 SHOW VARIABLES LIKE 'character_set_%'; 依次修改like出来的字段 例如 set character_set_results=utf8; 完了修改/e ...
- 使用Fiddler远程抓包(转载)
转载自 http://www.cnblogs.com/111testing/p/6436372.html Fiddler简介以及web抓包 一.Fiddler简介 简单来说,Fiddler是一个htt ...
- 把图片上的文字转换成word文字?
转换后的文字不是很如意,但是免费方便. 1.打开Office办公软件自带的OneNote工具.随便新建一个笔记页面,以方便我们接下来的操作. 2.插入图片.在菜单栏里点击[插入],选择插入[图片],找 ...
- SystemVerilog 带输出的task
1.task 的定义,输出定义为数组. /*- genRndPkt(): Generates random packet with the given length.*/ task genRndPkt ...
- Attention Please
关于BJJ与Matlab的学习时间安排在五六日晚间: 其余一切重心在学术!
- PHP面向对象之类的自动加载
类的自动加载 含义: 当某行代码需要一个类的时候,php的内部机制可以做到“自动加载该类文件”,以满足该行需要一个类的这种需求. 什么时候需要一个类? 1,new一个对象的时候: 2,使用一个类的静态 ...
- TZOJ 4871 文化之旅(floyd预处理+dfs剪枝)
描述 有一位使者要游历各国,他每到一个国家,都能学到一种文化,但他不愿意学习任何一种文化超过一次,即如果他学习了某种文化,则他就不能到达其他有这种文化的国家.不同的国家可能有相同的文化.不同文化的国家 ...