isinstance(obj,cls)和 issubclass(sub,super)
isinstance(obj,cls)检查对象obj是否是类cls的对象
issubclass(sub,super)检查类sub是否是类super的派生类
1 #isinstance 2 class Foo: 3 pass 4 f = Foo() 5 print(isinstance(f,Foo)) 6 # True 7 8 #issubclass 9 class Bar(Foo): 10 pass 11 print(issubclass(Bar,Foo)) 12 # True
1 class Foo: 2 pass 3 class Bar(Foo): 4 pass 5 b1 = Bar() 6 print(isinstance(b1,Foo)) 7 # True 8 print(type(b1)) 9 # <class '__main__.Bar'>
反射
什么是反射?
反射的概念有Smith在1982年首次提出,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(反省)。这一概念首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
四个可以实现自省的函数
下列方法适用于类和对象
判断是否包含某个数据属性或函数属性,没有则返回False
1 class Agency: 2 def __init__(self,name,addr): 3 self.name = name 4 self.addr = addr 5 6 def sell(self): 7 print("%s is selling an apartment"%self.name) 8 9 def rent(self): 10 print("%s is renting an apartment" % self.name) 11 12 a1 = Agency("Lianjia","downstairs") 13 print(a1.__dict__) 14 # {'name': 'Lianjia', 'addr': 'downstairs'} 15 print(hasattr(a1,"name")) 16 print(hasattr(a1,"sell")) 17 # True 18 # True
查找包含的某个数据属性或函数属性,没有则报错,Default = None
1 print(getattr(a1,"name")) 2 print(getattr(a1,"sell")) 3 func = getattr(a1,"sell") 4 func() 5 # Lianjia 6 # <bound method Agency.sell of <__main__.Agency object at 0x000001D350437C50>> 7 # Lianjia is selling an apartment 8 print(getattr(a1,"buy")) 9 # Wrong
添加或修改某个数据属性或函数属性
1 setattr(a1,"name","Jinxiu") 2 print(getattr(a1,"name")) 3 # Jinxiu 4 setattr(a1,"run",True) 5 print(a1.__dict__) 6 # {'name': 'Jinxiu', 'addr': 'downstairs', 'run': True}
删除某个数据属性或函数属性
1 delattr(a1,"run") 2 print(a1.__dict__) 3 # {'name': 'Jinxiu', 'addr': 'downstairs'}
为什么需要使用反射?
可以事先定义好接口,接口只在被完成后才会真正执行,这实现了即插即用,其实是一种“后期绑定”。即可以事先将主要逻辑写好(只定义接口),然后后期再去实现接口的功能。
动态导入模块
调到顶层模块
1 module_m = __import__("m.test") 2 print(module_m) 3 # <module 'm' (namespace)> #不管多少层,都返回最顶层模块 4 module_m.test1() 5 # Wrong 6 module_m.test.test1() 7 # test1
调到所需模块
1 import importlib 2 m = importlib.import_module("m.test") 3 print(m) 4 # <module 'm.test' from 'D:\PythonStudy\Python全栈\day26\m\test.py'>
类的内置attr属性
1 class Foo: 2 x = 10 3 def __init__(self,y): 4 self.y = y 5 def __getattr__(self,item): 6 print("getattr is running") 7 8 f = Foo(5) 9 print(f.x) 10 print(f.y) 11 f.selqengelegngq 12 # 10 13 # 5 14 # getattr is running
1 #删除会触发delattr 2 class Foo: 3 x = 10 4 def __init__(self,y): 5 self.y = y 6 def __delattr__(self,item): 7 print("delattr is running") 8 9 f = Foo(5) 10 del f.x 11 print(f.__dict__) 12 # delattr is running 13 # {'y': 5} 14 del f.y 15 # delattr is running
1 # 只要设置属性就会触发,死循环 2 class Foo: 3 x = 10 4 def __init__(self,y): 5 self.y = y 6 def __setattr__(self,key,value): 7 # self.key = value 只要设置就会触发,死循环 8 self.__dict__[key] = value #新增不会触发 9 print("setattr is running") 10 f1 = Foo(5) 11 print(f1.__dict__) 12 # setattr is running 13 # {'y': 5} 14 f1.z= 8 15 print(f1.__dict__) 16 # setattr is running 17 # {'y': 5, 'z': 8}
这些属性都是系统内置的,自己定义后会使用自己定义的函数, 否则就使用内置的函数
二次加工标准类型(包装)
包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生只是(其他的标准类型均可以通过下面的方式进行二次加工)
1 class List(list): 2 pass 3 l1 = list("Hello World!") 4 print(l1,type(l1)) 5 l1 = List("Hello World!") 6 print(l1,type(l1)) 7 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] <class 'list'> 8 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] <class '__main__.List'>
1 class List(list): 2 def append(self,p_obj): 3 if type(p_obj) is str: 4 super().append(p_obj) #list.append(self,p_obj) 5 else: 6 print("Only string input is tactic") 7 8 l1 = List("Hello World!") 9 l1.append("wewn") 10 print(l1) 11 # ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', 'wewn']
授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建、修改或删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法
1 class FileHandle(): 2 def __init__(self,filename,mode="r",encoding ="utf-8"): 3 self.file = open(filename,mode,encoding=encoding) 4 self.mode = mode 5 self.encoding = encoding 6 def __getattr__(self,item): 7 return getattr(self.file,item) 8 9 f1 = FileHandle("a.txt","w+") 10 f1.write("11111 ") 11 print(f1.__dict__) 12 print("==>",f1.read) #触发__getattr__ 13 # {'file': <_io.TextIOWrapper name='a.txt' mode='w+' encoding='utf-8'>, 'mode': 'w+', 'encoding': 'utf-8'} 14 # ==> <built-in method read of _io.TextIOWrapper object at 0x00000145B83DC708> 15 16 f1.write("abc ") 17 f1.seek(0) 18 print(f1.read()) 19 # 11111 20 # abc
1 import time 2 class FileHandle(): 3 def __init__(self,filename,mode="r",encoding ="utf-8"): 4 self.file = open(filename,mode,encoding=encoding) 5 self.mode = mode 6 self.encoding = encoding 7 def __getattr__(self,item): 8 return getattr(self.file,item) 9 def write(self,item): 10 t = time.strftime("%Y-%m-%d %X") 11 self.file.write("%s %s"%(t,item)) 12 13 f1 = FileHandle("a.txt","w+") 14 f1.write("11111 ") 15 f1.write("x ") 16 f1.write("y ") 17 f1.seek(0) 18 print(f1.read()) 19 # 2020-03-13 20:56:30 11111 20 # 2020-03-13 20:56:30 x 21 # 2020-03-13 20:56:30 y
__getattribute__
优先级高于__getattr__
1 class Foo: 2 def __init__(self,x): 3 self.x = x 4 def __getattr__(self,item): 5 print("getattr is running") 6 def __getattribute__(self,item): 7 print("getattribute is running") 8 f1 = Foo(1) 9 f1.x 10 # getattribute is running 11 f1.y 12 # getattribute is running
与raise AttributeError 结合使用,可以给getattr传递信息(即调用getattr)
1 class Foo: 2 def __init__(self,x): 3 self.x = x 4 def __getattr__(self,item): 5 print("getattr is running") 6 def __getattribute__(self,item): 7 print("getattribute is running") 8 raise AttributeError("Not Existed") 9 f1 = Foo(1) 10 f1.x 11 # getattribute is running 12 # getattr is running 13 f1.y 14 # getattribute is running 15 # getattr is running
描述符
1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
1 class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符 2 def __get__(self, instance, owner): 3 pass 4 def __set__(self, instance, value): 5 pass 6 def __delete__(self, instance): 7 pass
2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
1 class Foo: 2 def __get__(self, instance, owner): 3 print('触发get') 4 def __set__(self, instance, value): 5 print('触发set') 6 def __delete__(self, instance): 7 print('触发delete') 8 9 #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法 10 f1=Foo() 11 f1.name='egon' 12 f1.name 13 del f1.name
1 #描述符Str 2 class Str: 3 def __get__(self, instance, owner): 4 print('Str调用') 5 def __set__(self, instance, value): 6 print('Str设置...') 7 def __delete__(self, instance): 8 print('Str删除...') 9 10 #描述符Int 11 class Int: 12 def __get__(self, instance, owner): 13 print('Int调用') 14 def __set__(self, instance, value): 15 print('Int设置...') 16 def __delete__(self, instance): 17 print('Int删除...') 18 19 class People: 20 name=Str() 21 age=Int() 22 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 23 self.name=name 24 self.age=age 25 26 #何地?:定义成另外一个类的类属性 27 28 #何时?:且看下列演示 29 30 p1=People('alex',18) 31 # Str设置... 32 # Int设置... 33 34 #描述符Str的使用 35 p1.name 36 p1.name='egon' 37 del p1.name 38 # Str调用 39 # Str设置... 40 # Str删除... 41 42 #描述符Int的使用 43 p1.age 44 p1.age=18 45 del p1.age 46 # Int调用 47 # Int设置... 48 # Int删除... 49 50 #我们来瞅瞅到底发生了什么 51 print(p1.__dict__) 52 print(People.__dict__) 53 # {} 54 # {'__module__': '__main__', 'name': <__main__.Str object at 0x0000015757DC79E8>, 'age': <__main__.Int object at 0x0000015757DB1E80>, '__init__': <function People.__init__ at 0x0000015766F2A7B8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} 55 56 #补充 57 print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的 58 print(type(p1).__dict__ == People.__dict__) 59 # True 60 # True 61 #疑问:何时,何地,会触发这三个方法的执行
3 描述符分两种
—— 数据描述符:至少实现了__get__()和__set__()
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get')
—— 非数据描述符:没有实现__set__()
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
4 注意事项:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类
二、必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三、要严格遵循该优先级,优先级由高到底分别是
1.类属性
1 #描述符Str 2 class Str: 3 def __get__(self, instance, owner): 4 print('Str调用') 5 def __set__(self, instance, value): 6 print('Str设置...') 7 def __delete__(self, instance): 8 print('Str删除...') 9 10 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 13 self.name=name 14 self.age=age 15 16 17 #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典 18 19 #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错 20 People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__() 21 22 People.name='egon' #那赋值呢,我去,并没有触发__set__() 23 del People.name #赶紧试试del,我去,也没有触发__delete__() 24 # Str调用 25 #结论:描述符对类没有作用-------->错 26 27 ''' 28 原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级 29 People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__() 30 31 People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__() 32 del People.name #同上 33 '''
2.数据描述符
1 #描述符Str 2 class Str: 3 def __get__(self, instance, owner): 4 print('Str调用') 5 def __set__(self, instance, value): 6 print('Str设置...') 7 def __delete__(self, instance): 8 print('Str删除...') 9 10 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str类代理,age被Int类代理, 13 self.name=name 14 self.age=age 15 16 17 p1=People('egon',18) 18 19 #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性 20 p1.name='egonnnnnn' 21 p1.name 22 print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了 23 del p1.name 24 # Str设置... 25 # Str设置... 26 # Str调用 27 # {'age': 18} 28 # Str删除...
3.实例属性
1 class Foo: 2 def func(self): 3 print('我胡汉三又回来了') 4 f1=Foo() 5 f1.func() #调用类的方法,也可以说是调用非数据描述符 6 #函数是一个非数据描述符对象(一切皆对象么) 7 print(dir(Foo.func)) 8 print(hasattr(Foo.func,'__set__')) 9 print(hasattr(Foo.func,'__get__')) 10 print(hasattr(Foo.func,'__delete__')) 11 # 我胡汉三又回来了 12 # ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] 13 # False 14 # True 15 # False 16 #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了 17 #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么 18 #函数就是一个由非描述符类实例化得到的对象 19 #没错,字符串也一样 20 21 f1.func='这是实例属性啊' 22 print(f1.func) 23 #这是实例属性啊 24 del f1.func #删掉了非数据 25 f1.func() 26 # 我胡汉三又回来了
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get') 6 class Room: 7 name=Foo() 8 def __init__(self,name,width,length): 9 self.name=name 10 self.width=width 11 self.length=length 12 13 14 #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级 15 #对实例的属性操作,触发的都是描述符的 16 r1=Room('厕所',1,1) 17 r1.name 18 r1.name='厨房' 19 20 class Foo: 21 def __get__(self, instance, owner): 22 print('get') 23 class Room: 24 name=Foo() 25 def __init__(self,name,width,length): 26 self.name=name 27 self.width=width 28 self.length=length 29 30 31 #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级 32 #对实例的属性操作,触发的都是实例自己的 33 r1=Room('厕所',1,1) 34 r1.name 35 r1.name='厨房' 36 # set 37 # get 38 # set
4.非数据描述符
1 class Foo: 2 def func(self): 3 print('我胡汉三又回来了') 4 5 def __getattr__(self, item): 6 print('找不到了当然是来找我啦',item) 7 f1=Foo() 8 9 f1.xxxxxxxxxxx 10 # 找不到了当然是来找我啦 xxxxxxxxxxx
5.找不到的属性触发__getattr__()
5 描述符使用
众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制
1 #描述符进行类型检测 2 class Typed: 3 def __init__(self,key): 4 self.key = key 5 def __get__(self,instance,owner): 6 print("get method") 7 # print("instance参数[%s]"%instance) 8 # print("owner参数[%s]" %owner) 9 return instance.__dict__[self.key] 10 def __set__(self, instance, value): 11 print("set method") 12 # print("instance参数[%s]" % instance) 13 # print("value参数[%s]" % value) 14 if not isinstance(value,str): 15 # print("请传入字符串") 16 # return 17 raise TypeError("你传入的不是字符串") 18 instance.__dict__[self.key] = value 19 def __delete__(self,instance): 20 print("delete method") 21 # print("instance参数[%s]" % instance) 22 instance.__dict__.pop(self.key) 23 24 25 class People: 26 name = Typed("name") #触发了Typed的set 27 def __init__(self,name,age,salary): 28 self.name = name #触发set 29 self.age = age 30 self.salary = salary 31 32 p1 = People("Jenny",20,30.5) 33 p1.name 34 print(p1.name) 35 # set method 36 # get method 37 # get method 38 # Jenny 39 print(p1.__dict__) 40 p1.name = "ALLY" 41 print(p1.__dict__) 42 # {'name': 'Jenny', 'age': 20, 'salary': 30.5} 43 # set method 44 # {'name': 'ALLY', 'age': 20, 'salary': 30.5} 45 del p1.name 46 print(p1.__dict__) 47 # delete method 48 # {'age': 20, 'salary': 30.5} 49 p1.name = 18 50 print(p1.__dict__) 51 # # set method 52 # # 请传入字符串 53 # # {'age': 20, 'salary': 30.5} 54 # set method 55 # raise TypeError("你传入的不是字符串") 56 # TypeError: 你传入的不是字符串 57 58 #没有类型检测 59 # def test(x): 60 # print("===>",x) 61 # test("ally") 62 # test(1) 63 # # ===> ally 64 # # ===> 1
大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现
也可以一步到位
1 class Typed: 2 def __init__(self,key,expected_type): 3 self.key = key 4 self.expected_type = expected_type 5 def __get__(self,instance,owner): 6 print("get method") 7 # print("instance参数[%s]"%instance) 8 # print("owner参数[%s]" %owner) 9 return instance.__dict__[self.key] 10 def __set__(self, instance, value): 11 print("set method") 12 # print("instance参数[%s]" % instance) 13 # print("value参数[%s]" % value) 14 if not isinstance(value,self.expected_type): 15 # print("请传入字符串") 16 # return 17 raise TypeError("你传入的不是",self.expected_type) 18 instance.__dict__[self.key] = value 19 def __delete__(self,instance): 20 print("delete method") 21 # print("instance参数[%s]" % instance) 22 instance.__dict__.pop(self.key) 23 24 25 class People: 26 name = Typed("name",str) #触发了Typed的set 27 age = Typed("age", int) 28 salary = Typed("salary", float) 29 def __init__(self,name,age,salary): 30 self.name = name #触发set 31 self.age = age 32 self.salary = salary 33 34 p1 = People("Jenny",20,30.5) 35 # p2 = People('Arron',"twenty",15) 36 p3 = People("Fahrenheit",30,"40")
与装饰器结合使用
1 class Typed: 2 def __init__(self,key,expected_type): 3 self.key = key 4 self.expected_type = expected_type 5 def __get__(self,instance,owner): 6 return instance.__dict__[self.key] 7 def __set__(self, instance, value): 8 if not isinstance(value,self.expected_type): 9 # print("请传入字符串") 10 # return 11 raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type)) 12 instance.__dict__[self.key] = value 13 def __delete__(self,instance): 14 instance.__dict__.pop(self.key) 15 16 def deco(**kwargs): 17 def wrapper(obj): 18 for key,value in kwargs.items(): 19 setattr(obj,key,Typed(key,value)) 20 return obj 21 return wrapper 22 23 @deco(name=str,age=int,gender=str,salary=float) 24 class People: 25 def __init__(self,name,age,gender,salary): 26 self.name = name #触发set 27 self.age = age 28 self.gender = gender 29 self.salary = salary 30 31 p1 = People("Jenny",20,"female",90) 32 print(People.__dict__) 33 p2 = People('Arron',"twenty","male",500)
6 描述符总结
描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)
1 class Lazyproperty: 2 def __init__(self,func): 3 print("------",func) 4 self.func = func 5 6 class Room: 7 def __init__(self,name,width,length): 8 self.name = name 9 self.width = width 10 self.length = length 11 @Lazyproperty #area = Lazyproperty(area) 12 def area(self): 13 return self.width*self.length 14 r1 = Room("Bedroom",3,5) 15 # ------ <function Room.area at 0x000001B7043AA598>
1 class Lazyproperty: 2 def __init__(self,func): 3 print("------",func) 4 self.func = func 5 def __get__(self,instance,owner): 6 print("get") 7 res = self.func(instance) 8 return res 9 class Room: 10 def __init__(self,name,width,length): 11 self.name = name 12 self.width = width 13 self.length = length 14 15 @Lazyproperty #area = Lazyproperty(area) 16 def area(self): 17 return self.width*self.length 18 r1 = Room("Bedroom",3,5) 19 print(r1.__dict__) 20 print(r1.area) 21 # ------ <function Room.area at 0x000001DA1DA5A620> 22 # {'name': 'Bedroom', 'width': 3, 'length': 5} 23 # get 24 # 15
1 class Lazyproperty: 2 def __init__(self,func): 3 self.func = func 4 def __get__(self,instance,owner): 5 if instance is None: 6 return self 7 res = self.func(instance) 8 setattr(instance,self.func.__name__,res) 9 return res 10 class Room: 11 def __init__(self,name,width,length): 12 self.name = name 13 self.width = width 14 self.length = length 15 @Lazyproperty #area = Lazyproperty(area) 16 def area(self): 17 return self.width*self.length 18 r1 = Room("Bedroom",3,5) 19 print(r1.__dict__) 20 # {'name': 'Bedroom', 'width': 3, 'length': 5} 21 print(r1.area) 22 # 15 23 print(r1.__dict__) 24 # {'name': 'Bedroom', 'width': 3, 'length': 5, 'area': 15}
8 利用描述符原理完成一个自定制@classmethod
9 利用描述符原理完成一个自定制的@staticmethod
__enter__和__exit__
在操作文件时可以用
1 with open("a.txt") as f: 2 "代码块"
上述叫做上下文管理协议,即with语句,为了让一个对象兼容
with obj ==>触发obj.__enter__(),拿到返回值
as f ===>f = 返回值
with obj as f <===>f = obj.__enter__()
执行代码块:
1、无异常,整个代码块运行完毕后去触发__exit__,它的三个参数都为None
2、有异常,从异常出现的位置直接触发__exit__
a.如果__exit__的返回值为True,代表吞掉异常
b.如果__exit__的返回值不为True,代表吐出异常
c.__exit__运行完毕就代表了整个with语句执行完毕
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 print("enter is running") 6 return self 7 def __exit__(self,exc_type,exc_val,exc_tb): 8 print("exit is running") 9 # f1 = Open("a.txt") 不能这样使用 10 with Open("a.txt") as f: #触发enter,拿到enter return的结果 11 print(f.name) 12 print("====>") 13 print("====>") 14 print("====>") 15 16 print("*******") 17 # enter is running #enter在打开时运行 18 # a.txt 19 # ====> 20 # ====> 21 # ====> 22 # exit is running #exit在文件运行后运行 23 # *******
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 print("enter is running") 6 return self 7 def __exit__(self,exc_type,exc_val,exc_tb): 8 print("exit is running") 9 print(exc_type) 10 print(exc_val) 11 print(exc_tb) 12 # f1 = Open("a.txt") 不能这样使用 13 with Open("a.txt") as f: #触发enter,拿到enter return的结果 14 print(f.name) 15 print(f.x) 16 print(f) 17 # exit的三个信息在触发异常时分别代表异常的种类、值和traceback信息 18 # exit的三个信息在无异常时返回None
加上return True就可以不报错
1 class Open: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 print("enter is running") 6 return self 7 def __exit__(self,exc_type,exc_val,exc_tb): 8 print("exit is running") 9 print(exc_type) 10 print(exc_val) 11 print(exc_tb) 12 return True #这样就不会报错了 13 # f1 = Open("a.txt") 不能这样使用 14 with Open("a.txt") as f: #触发enter,拿到enter return的结果 15 print(f.name) 16 print(f.x) #触发exit函数了 17 print(f) #exit执行完毕就结束了,不会继续运行with语句了 18 # enter is running 19 # a.txt 20 # exit is running 21 # <class 'AttributeError'> 22 # 'Open' object has no attribute 'x' 23 # <traceback object at 0x000001CCFCC810C8>
用途:
1、使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动
2、在需要管理一些资源比如文件、网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无需再去关注该问题,大有用处
类的装饰器
回顾一下函数装饰器
1 #函数装饰器 2 def deco(func): 3 print("=====") 4 return func 5 @deco #fun = deco(fun) 6 def fun(): 7 print("*****") 8 # ===== 9 fun() 10 # *****
类的装饰器
1 #类装饰器 2 def deco(obj): 3 print("=====") 4 return obj 5 6 @deco #Foo = deco(Foo) 7 class Foo: 8 print("$$$$$") 9 f1 = Foo() 10 print(f1) 11 # $$$$$ 12 # ===== 13 # <__main__.Foo object at 0x0000024FE9D052B0>
装饰器增强版
1 def typed(**kwargs): 2 def deco(obj): 3 for key,value in kwargs.items(): 4 setattr(obj,key,value) 5 return obj 6 return deco 7 8 @typed(x = 1, y = 2, z= 3) #1.typed(x = 1, y = 2, z= 3) 2.@deco Foo=deco(foo) 9 class Foo: 10 pass 11 print(Foo.x,Foo.y,Foo.z) 12 # 1 2 3 13 14 @typed(name = "alex") 15 class Bar: 16 pass 17 print(Bar.name) 18 # alex 19 20 装饰器增强
回到描述器应用
1 class Typed: 2 def __init__(self,key,expected_type): 3 self.key = key 4 self.expected_type = expected_type 5 def __get__(self,instance,owner): 6 return instance.__dict__[self.key] 7 def __set__(self, instance, value): 8 if not isinstance(value,self.expected_type): 9 # print("请传入字符串") 10 # return 11 raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type)) 12 instance.__dict__[self.key] = value 13 def __delete__(self,instance): 14 instance.__dict__.pop(self.key) 15 16 def deco(**kwargs): 17 def wrapper(obj): 18 for key,value in kwargs.items(): 19 setattr(obj,key,Typed(key,value)) 20 return obj 21 return wrapper 22 23 @deco(name=str,age=int,gender=str,salary=float) 24 class People: 25 def __init__(self,name,age,gender,salary): 26 self.name = name #触发set 27 self.age = age 28 self.gender = gender 29 self.salary = salary 30 31 p1 = People("Jenny",20,"female",90) 32 print(People.__dict__) 33 p2 = People('Arron',"twenty","male",500) 34 35 结合装饰器应用
getitem 、setitem 和delitem
item只适合于字典的操作,attr适合用.方式操作
1 class Foo: 2 def __getitem__(self,item): 3 print("getitem",item) 4 def __setitem__(self,key,value): 5 self.__dict__[key]=value 6 print("setitem") 7 def __delitem__(self,key): 8 print("delitem") 9 self.__dict__.pop(key) 10 11 f = Foo() 12 f["name"] = "Jenny" 13 f["age"] = 18 14 f["gender"] = "female" 15 print(f.__dict__) 16 # setitem 17 # setitem 18 # {'name': 'Jenny', 'age': 18} 19 del f.age 20 print(f.__dict__) 21 {'name': 'Jenny'} 22 del f["name"] 23 # delitem 24 f.gender 25 f["gender"] 26 # getitem gender
str和repr
改变对象的字符串显示,使用print
1 # 定义str 2 class Foo: 3 def __init__(self,name,age): 4 self.name = name 5 self.age = age 6 def __str__(self): 7 return "名字是%s,年纪是%s"%(self.name,self.age) 8 9 f = Foo("Jenny",18) 10 print(f) 11 # 名字是Jenny,年纪是18 12 13 # 不定义str 14 class Foo: 15 def __init__(self,name,age): 16 self.name = name 17 self.age = age 18 f = Foo("Jenny", 18) 19 print(f) 20 # <__main__.Foo object at 0x00000242D9E4FB70>
repr用于解释器中
二者共存的效果
1 class Foo: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 def __str__(self): 6 return self.name 7 def __repr__(self): 8 return "名字是%s,年纪是%s"%(self.name,self.age) 9 10 f = Foo("Jenny",18) #str(f)= f.__str__ 找不到才=> f.__repr__ 11 print(f) 12 # Jenny
自定义format
1 # 不定义自身的format函数 2 class Date: 3 def __init__(self,year,mon,day): 4 self.year = year 5 self.mon = mon 6 self.day = day 7 d1= Date(2018,10,26) 8 x = "{0.year}{0.mon}{0.day}".format(d1) 9 print(x) 10 # 20181026 11 12 # 定义自身的format函数 13 format_dic = { 14 "ymd":"{0.year}{0.mon}{0.day}", 15 "mdy":"{0.mon}:{0.day}:{0.year}", 16 "dmy":"{0.day}-{0.mon}-{0.year}" 17 } 18 class Date: 19 def __init__(self,year,mon,day): 20 self.year = year 21 self.mon = mon 22 self.day = day 23 def __format__(self,format_spec): 24 print("running") 25 if not format_spec or format_spec not in format_dic: 26 format_spec = "ymd" 27 fm = format_dic[format_spec] 28 return fm.format(self) 29 30 d1= Date(2018,10,26) 31 print(format(d1)) 32 print(format(d1,"mdy")) 33 # running 34 # 20181026 35 # running 36 # 10:26:2018
slots属性
1.slots是什么? 是一个类变量,变量值可以是列表、元组或可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,是能使用在__slots__中定义的那些属性名。
4.注意:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大部分情况,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如:在程序中需要创建某个类的几百万个实例对象。
关于__slots__的常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这种目的,但这不是它的使用初衷,更多是作为一个内存优化的工具。
1 class Foo: 2 __slots__ = "name" 3 f1 = Foo() 4 f1.name = "Dragon" 5 print(f1.name) 6 # Dragon 7 # f1.age = 18 #已经没有fi.__dict__了 8 # # Wrong
1 class Foo: 2 __slots__ = ["name","age"] 3 f1 = Foo() 4 f1.name = "Dragon" 5 f1.age = 18 6 print(f1.__slots__) 7 print(f1.age) 8 # ['name', 'age'] 9 # 18 10 f2 = Foo() 11 f2.age = 90 12 print(f2.age) 13 # 90
doc属性
获取文档信息,该属性无法被继承
1 class Foo: 2 "描述信息" 3 pass 4 print(Foo.__doc__) 5 # 描述信息
__module__和 __class__
__module__查看来自哪个模块
__class__查看来自哪个类
1 from aa.bb import Foo 2 f1 = Foo() 3 print(f1.__module__) 4 print(f1.__class__) 5 # ABCD 6 # aa.bb #模块信息 7 # <class 'aa.bb.Foo'> #类信息
__del__析构方法
当对象在内存中被释放时,自动触 发执行。
注:此方法一般无需定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交由Python解释器执行的,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
1 class Foo: 2 def __init__(self,name): 3 self.name = name 4 def __del__(self): 5 print("del is running") 6 f1=Foo("alex") 7 del f1 8 print("=====>") 9 # del is running #删除实例所以触发__del__ 10 # =====> #执行完毕继续运行后面的程序
del只在实例被删除的时候被触发,而不是属性被删除就触发
1 class Foo: 2 def __init__(self,name): 3 self.name = name 4 def __del__(self): 5 print("del is running") 6 f1=Foo("alex") 7 del f1.name 8 print("=====>") 9 # =====> #没有先触发__del__而是先执行后面的程序 10 # del is running #整个程序执行完毕内存释放所以__del__运行
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名(); 对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()
1 class Foo: 2 def __call__(self): 3 print("ABCD") 4 f1 = Foo() #创建了实例 5 f1() # Foo下的__call__ 6 # ABCD
__next__ 和__iter__实现迭代器协议
从迭代器协议讲起
1 class Foo: 2 def __init__(self,n): 3 self.n = n 4 def __iter__(self): 5 return self 6 def __next__(self): 7 self.n += 1 8 return self.n 9 f1= Foo(10) 10 print(f1.__next__()) 11 print(f1.__next__()) 12 print(next(f1)) 13 print(next(f1)) 14 # 11 15 # 12 16 # 13 17 # 14
1 class Fib: 2 def __init__(self): 3 self._a = 1 4 self._b = 1 5 6 def __iter__(self): 7 return self 8 9 def __next__(self): 10 if self._a > 100: 11 raise StopIteration("终止") 12 self._a, self._b = self._b, self._a + self._b 13 return self._a 14 f1 = Fib() 15 print(next(f1)) 16 print(next(f1)) 17 print(next(f1)) 18 print(next(f1)) 19 print(next(f1)) 20 print(next(f1)) 21 print("====>") 22 for i in f1: 23 print(i) 24 # 1 25 # 2 26 # 3 27 # 5 28 # 8 29 # 13 30 # ====> 31 # 21 32 # 34 33 # 55 34 # 89 35 # 144
property补充
1 class Foo: 2 @property 3 def AAA(self): 4 print('get AAA') 5 @AAA.setter 6 def AAA(self,value): 7 print("set AAA") 8 @AAA.deleter 9 def AAA(self): 10 print("delete AAA") 11 12 f1=Foo() 13 f1.AAA='aaa' 14 del f1.AAA 15 # set AAA 16 # delete AAA
这种效果也一样
1 class Foo: 2 def get_AAA(self): #这几个函数的顺序不可改变 3 print('get AAA') 4 def set_AAA(self,value): 5 print("set AAA") 6 def del_AAA(self): 7 print("delete AAA") 8 AAA=property(get_AAA,set_AAA,del_AAA) 9 f1=Foo() 10 f1.AAA='aaa' 11 del f1.AAA 12 # set AAA 13 # delete AAA
metaclass 元类
1、引子
Python中一切皆对象,类本身也是一个对象。当使用关键字class时,python解释器在加载class的时候就会创建出一个对象(这里的对象指的是类而非类的示例)
1 class Foo: 2 pass 3 f1 = Foo() 4 print(type(Foo)) 5 print(type(f1)) 6 # <class 'type'> #类的类就是type 7 # <class '__main__.Foo'> #表示obj对象由Foo类创建
2、什么是元类
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的示例为类,正如类的实例为对象
type是python内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3、创建类的两种方式
1 class Foo: 2 def __init__(self,name): 3 self.name = name 4 print(Foo) 5 def __init__(self,name): 6 self.name = name 7 def test(self): 8 print("test------") 9 FFo = type("FFo",(object,),{"x":1,"__init__":__init__,"test":test}) 10 print(FFo) 11 # <class '__main__.Foo'> 12 # <class '__main__.FFo'> 13 f1 = FFo("alex") 14 print(f1.x) 15 # 1 16 f1.test() 17 # test------
4、一个类没有声明自己的元类,则默认其元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类
1 class MyType(type): 2 def __init__(self,a,b,c): 3 print("我的元类") 4 print(a) #类名 5 print(b) #继承的东西 6 print(c) #属性字典 7 def __call__(self, *args, **kwargs): 8 print("====>") 9 obj = object.__new__(self) #object.__new__(Foo)->f1 10 self.__init__(obj,*args, **kwargs) #Foo.__init__(f1,*args, **kwargs) 11 return obj 12 class Foo(metaclass=MyType): #MyType("Foo",(object,),{})触发init 13 def __init__(self,name): 14 self.name = name 15 print(Foo) 16 f1=Foo("alex") 17 print(f1.__dict__) 18 # 我的元类 19 # Foo 20 # () 21 # {'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x0000025E46C79598>} 22 # <class '__main__.Foo'> 23 # ====> 24 # {'name': 'alex'}