isinstance(obj,cls)和 issubclass(sub,super)
isinstance(obj,cls)检查对象obj是否是类cls的对象
issubclass(sub,super)检查类sub是否是类super的派生类


1 #isinstance
2 classFoo:3 pass
4 f =Foo()5 print(isinstance(f,Foo))6 #True
7
8 #issubclass
9 classBar(Foo):10 pass
11 print(issubclass(Bar,Foo))12 #True
isinstance&issubclass


1 classFoo:2 pass
3 classBar(Foo):4 pass
5 b1 =Bar()6 print(isinstance(b1,Foo))7 #True
8 print(type(b1))9 #
继承的种类关系
反射
什么是反射?
反射的概念有Smith在1982年首次提出,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(反省)。这一概念首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
四个可以实现自省的函数
下列方法适用于类和对象
判断是否包含某个数据属性或函数属性,没有则返回False


1 classAgency:2 def __init__(self,name,addr):3 self.name =name4 self.addr =addr5
6 defsell(self):7 print("%s is selling an apartment"%self.name)8
9 defrent(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
hasattr
查找包含的某个数据属性或函数属性,没有则报错,Default = None


1 print(getattr(a1,"name"))2 print(getattr(a1,"sell"))3 func = getattr(a1,"sell")4 func()5 #Lianjia
6 #>
7 #Lianjia is selling an apartment
8 print(getattr(a1,"buy"))9 #Wrong
getattr
添加或修改某个数据属性或函数属性


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}
setattr
删除某个数据属性或函数属性


1 delattr(a1,"run")2 print(a1.__dict__)3 #{'name': 'Jinxiu', 'addr': 'downstairs'}
delattr
为什么需要使用反射?
可以事先定义好接口,接口只在被完成后才会真正执行,这实现了即插即用,其实是一种“后期绑定”。即可以事先将主要逻辑写好(只定义接口),然后后期再去实现接口的功能。
动态导入模块
调到顶层模块
1 module_m = __import__("m.test")2 print(module_m)3 # #不管多少层,都返回最顶层模块
4 module_m.test1()5 #Wrong
6 module_m.test.test1()7 #test1
调到所需模块
1 importimportlib2 m = importlib.import_module("m.test")3 print(m)4 #
类的内置attr属性


1 classFoo:2 x = 10
3 def __init__(self,y):4 self.y =y5 def __getattr__(self,item):6 print("getattr is running")7
8 f = Foo(5)9 print(f.x)10 print(f.y)11 f.selqengelegngq12 #10
13 #5
14 #getattr is running
getattr


1 #删除会触发delattr
2 classFoo:3 x = 10
4 def __init__(self,y):5 self.y =y6 def __delattr__(self,item):7 print("delattr is running")8
9 f = Foo(5)10 delf.x11 print(f.__dict__)12 #delattr is running
13 #{'y': 5}
14 delf.y15 #delattr is running
delattr


1 #只要设置属性就会触发,死循环
2 classFoo:3 x = 10
4 def __init__(self,y):5 self.y =y6 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}
setattr
这些属性都是系统内置的,自己定义后会使用自己定义的函数, 否则就使用内置的函数
二次加工标准类型(包装)
包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生只是(其他的标准类型均可以通过下面的方式进行二次加工)


1 classList(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', '!'] 
8 #['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'] 
继承的种类和原种类


1 classList(list):2 defappend(self,p_obj):3 if type(p_obj) isstr: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 classFileHandle():2 def __init__(self,filename,mode="r",encoding ="utf-8"):3 self.file = open(filename,mode,encoding=encoding)4 self.mode =mode5 self.encoding =encoding6 def __getattr__(self,item):7 returngetattr(self.file,item)8
9 f1 = FileHandle("a.txt","w+")10 f1.write("11111\n")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 #==> 
15
16 f1.write("abc\n")17 f1.seek(0)18 print(f1.read())19 #11111
20 #abc
文件


1 importtime2 classFileHandle():3 def __init__(self,filename,mode="r",encoding ="utf-8"):4 self.file = open(filename,mode,encoding=encoding)5 self.mode =mode6 self.encoding =encoding7 def __getattr__(self,item):8 returngetattr(self.file,item)9 defwrite(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\n")15 f1.write("x\n")16 f1.write("y\n")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 classFoo:2 def __init__(self,x):3 self.x =x4 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.x10 #getattribute is running
11 f1.y12 #getattribute is running
无论属性是否存在都运行getattribute
与raise AttributeError 结合使用,可以给getattr传递信息(即调用getattr)


1 classFoo:2 def __init__(self,x):3 self.x =x4 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.x11 #getattribute is running
12 #getattr is running
13 f1.y14 #getattribute is running
15 #getattr is running
getattribute与raise异常处理
描述符
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 classFoo: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.name13 del f1.name
描述符类产生的实例进行属性操作并不会触发三个方法的执行


1 #描述符Str
2 classStr: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 classInt: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 classPeople:20 name=Str()21 age=Int()22 def __init__(self,name,age): #name被Str类代理,age被Int类代理,
23 self.name=name24 self.age=age25
26 #何地?:定义成另外一个类的类属性
27
28 #何时?:且看下列演示
29
30 p1=People('alex',18)31 #Str设置...
32 #Int设置...
33
34 #描述符Str的使用
35 p1.name36 p1.name='egon'
37 delp1.name38 #Str调用
39 #Str设置...
40 #Str删除...
41
42 #描述符Int的使用
43 p1.age44 p1.age=18
45 delp1.age46 #Int调用
47 #Int设置...
48 #Int删除...
49
50 #我们来瞅瞅到底发生了什么
51 print(p1.__dict__)52 print(People.__dict__)53 #{}
54 #{'__module__': '__main__', 'name': <__main__.str object at>, 'age': <__main__.int object at>, '__init__': , '__dict__': , '__weakref__': , '__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 classFoo:2 def __set__(self, instance, value):3 print('set')4 def __get__(self, instance, owner):5 print('get')
—— 非数据描述符:没有实现__set__()
1 classFoo:2 def __get__(self, instance, owner):3 print('get')
4 注意事项:
一、描述符本身应该定义成新式类,被代理的类也应该是新式类
二、必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三、要严格遵循该优先级,优先级由高到底分别是
1.类属性


1 #描述符Str
2 classStr: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 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str类代理,age被Int类代理,
13 self.name=name14 self.age=age15
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 classStr: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 classPeople:11 name=Str()12 def __init__(self,name,age): #name被Str类代理,age被Int类代理,
13 self.name=name14 self.age=age15
16
17 p1=People('egon',18)18
19 #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
20 p1.name='egonnnnnn'
21 p1.name22 print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
23 delp1.name24 #Str设置...
25 #Str设置...
26 #Str调用
27 #{'age': 18}
28 #Str删除...
数据描述符>实例属性
3.实例属性


1 classFoo:2 deffunc(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 classFoo:2 def __set__(self, instance, value):3 print('set')4 def __get__(self, instance, owner):5 print('get')6 classRoom:7 name=Foo()8 def __init__(self,name,width,length):9 self.name=name10 self.width=width11 self.length=length12
13
14 #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
15 #对实例的属性操作,触发的都是描述符的
16 r1=Room('厕所',1,1)17 r1.name18 r1.name='厨房'
19
20 classFoo:21 def __get__(self, instance, owner):22 print('get')23 classRoom:24 name=Foo()25 def __init__(self,name,width,length):26 self.name=name27 self.width=width28 self.length=length29
30
31 #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
32 #对实例的属性操作,触发的都是实例自己的
33 r1=Room('厕所',1,1)34 r1.name35 r1.name='厨房'
36 #set
37 #get
38 #set
实例属性>非数据描述符2
4.非数据描述符


1 classFoo:2 deffunc(self):3 print('我胡汉三又回来了')4
5 def __getattr__(self, item):6 print('找不到了当然是来找我啦',item)7 f1=Foo()8
9 f1.xxxxxxxxxxx10 #找不到了当然是来找我啦 xxxxxxxxxxx
非数据描述符>找不到
5.找不到的属性触发__getattr__()
5 描述符使用
众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制


1 #描述符进行类型检测
2 classTyped:3 def __init__(self,key):4 self.key =key5 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 notisinstance(value,str):15 #print("请传入字符串")
16 #return
17 raise TypeError("你传入的不是字符串")18 instance.__dict__[self.key] =value19 def __delete__(self,instance):20 print("delete method")21 #print("instance参数[%s]" % instance)
22 instance.__dict__.pop(self.key)23
24
25 classPeople:26 name = Typed("name") #触发了Typed的set
27 def __init__(self,name,age,salary):28 self.name = name #触发set
29 self.age =age30 self.salary =salary31
32 p1 = People("Jenny",20,30.5)33 p1.name34 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 delp1.name46 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
判断name 的str类
大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现
也可以一步到位


1 classTyped:2 def __init__(self,key,expected_type):3 self.key =key4 self.expected_type =expected_type5 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 notisinstance(value,self.expected_type):15 #print("请传入字符串")
16 #return
17 raise TypeError("你传入的不是",self.expected_type)18 instance.__dict__[self.key] =value19 def __delete__(self,instance):20 print("delete method")21 #print("instance参数[%s]" % instance)
22 instance.__dict__.pop(self.key)23
24
25 classPeople: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 =age32 self.salary =salary33
34 p1 = People("Jenny",20,30.5)35 #p2 = People('Arron',"twenty",15)
36 p3 = People("Fahrenheit",30,"40")
一步到位判断
与装饰器结合使用


1 classTyped:2 def __init__(self,key,expected_type):3 self.key =key4 self.expected_type =expected_type5 def __get__(self,instance,owner):6 return instance.__dict__[self.key]7 def __set__(self, instance, value):8 if notisinstance(value,self.expected_type):9 #print("请传入字符串")
10 #return
11 raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type))12 instance.__dict__[self.key] =value13 def __delete__(self,instance):14 instance.__dict__.pop(self.key)15
16 def deco(**kwargs):17 defwrapper(obj):18 for key,value inkwargs.items():19 setattr(obj,key,Typed(key,value))20 returnobj21 returnwrapper22
23 @deco(name=str,age=int,gender=str,salary=float)24 classPeople:25 def __init__(self,name,age,gender,salary):26 self.name = name #触发set
27 self.age =age28 self.gender =gender29 self.salary =salary30
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 classLazyproperty:2 def __init__(self,func):3 print("------",func)4 self.func =func5
6 classRoom:7 def __init__(self,name,width,length):8 self.name =name9 self.width =width10 self.length =length11 @Lazyproperty #area = Lazyproperty(area)
12 defarea(self):13 return self.width*self.length14 r1 = Room("Bedroom",3,5)15 #------ 
简单的property


1 classLazyproperty:2 def __init__(self,func):3 print("------",func)4 self.func =func5 def __get__(self,instance,owner):6 print("get")7 res =self.func(instance)8 returnres9 classRoom:10 def __init__(self,name,width,length):11 self.name =name12 self.width =width13 self.length =length14
15 @Lazyproperty #area = Lazyproperty(area)
16 defarea(self):17 return self.width*self.length18 r1 = Room("Bedroom",3,5)19 print(r1.__dict__)20 print(r1.area)21 #------ 
22 #{'name': 'Bedroom', 'width': 3, 'length': 5}
23 #get
24 #15
自定义property求面积


1 classLazyproperty:2 def __init__(self,func):3 self.func =func4 def __get__(self,instance,owner):5 if instance isNone:6 returnself7 res =self.func(instance)8 setattr(instance,self.func.__name__,res)9 returnres10 classRoom:11 def __init__(self,name,width,length):12 self.name =name13 self.width =width14 self.length =length15 @Lazyproperty #area = Lazyproperty(area)
16 defarea(self):17 return self.width*self.length18 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 classOpen:2 def __init__(self,name):3 self.name =name4 def __enter__(self):5 print("enter is running")6 returnself7 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 #*******
上下文协议-enter


1 classOpen:2 def __init__(self,name):3 self.name =name4 def __enter__(self):5 print("enter is running")6 returnself7 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
exit
加上return True就可以不报错


1 classOpen:2 def __init__(self,name):3 self.name =name4 def __enter__(self):5 print("enter is running")6 returnself7 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 #
22 #'Open' object has no attribute 'x'
23 #
exit+return True
用途:
1、使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无需手动
2、在需要管理一些资源比如文件、网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无需再去关注该问题,大有用处
类的装饰器
回顾一下函数装饰器
1 #函数装饰器
2 defdeco(func):3 print("=====")4 returnfunc5 @deco #fun = deco(fun)
6 deffun():7 print("*****")8 #=====
9 fun()10 #*****
类的装饰器
1 #类装饰器
2 defdeco(obj):3 print("=====")4 returnobj5
6 @deco #Foo = deco(Foo)
7 classFoo:8 print("$$$$$")9 f1 =Foo()10 print(f1)11 #$$$$$
12 #=====
13 #<__main__.foo object at>
装饰器增强版


1 def typed(**kwargs):2 defdeco(obj):3 for key,value inkwargs.items():4 setattr(obj,key,value)5 returnobj6 returndeco7
8 @typed(x = 1, y = 2, z= 3) #1.typed(x = 1, y = 2, z= 3) 2.@deco Foo=deco(foo)
9 classFoo:10 pass
11 print(Foo.x,Foo.y,Foo.z)12 #1 2 3
13
14 @typed(name = "alex")15 classBar:16 pass
17 print(Bar.name)18 #alex
19
20 装饰器增强
装饰器增强
回到描述器应用


1 classTyped:2 def __init__(self,key,expected_type):3 self.key =key4 self.expected_type =expected_type5 def __get__(self,instance,owner):6 return instance.__dict__[self.key]7 def __set__(self, instance, value):8 if notisinstance(value,self.expected_type):9 #print("请传入字符串")
10 #return
11 raise TypeError("%s你传入的不是%s"%(self.key,self.expected_type))12 instance.__dict__[self.key] =value13 def __delete__(self,instance):14 instance.__dict__.pop(self.key)15
16 def deco(**kwargs):17 defwrapper(obj):18 for key,value inkwargs.items():19 setattr(obj,key,Typed(key,value))20 returnobj21 returnwrapper22
23 @deco(name=str,age=int,gender=str,salary=float)24 classPeople:25 def __init__(self,name,age,gender,salary):26 self.name = name #触发set
27 self.age =age28 self.gender =gender29 self.salary =salary30
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 classFoo:2 def __getitem__(self,item):3 print("getitem",item)4 def __setitem__(self,key,value):5 self.__dict__[key]=value6 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 delf.age20 print(f.__dict__)21 {'name': 'Jenny'}22 del f["name"]23 #delitem
24 f.gender25 f["gender"]26 #getitem gender
item系列使用字典操作
str和repr
改变对象的字符串显示,使用print


1 #定义str
2 classFoo:3 def __init__(self,name,age):4 self.name =name5 self.age =age6 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 classFoo:15 def __init__(self,name,age):16 self.name =name17 self.age =age18 f = Foo("Jenny", 18)19 print(f)20 #<__main__.foo object at>
str
repr用于解释器中

二者共存的效果


1 classFoo:2 def __init__(self,name,age):3 self.name =name4 self.age =age5 def __str__(self):6 returnself.name7 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
str优先repr
自定义format


1 #不定义自身的format函数
2 classDate:3 def __init__(self,year,mon,day):4 self.year =year5 self.mon =mon6 self.day =day7 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 classDate:19 def __init__(self,year,mon,day):20 self.year =year21 self.mon =mon22 self.day =day23 def __format__(self,format_spec):24 print("running")25 if not format_spec or format_spec not informat_dic:26 format_spec = "ymd"
27 fm =format_dic[format_spec]28 returnfm.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
自定义format
slots属性
1.slots是什么? 是一个类变量,变量值可以是列表、元组或可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,是能使用在__slots__中定义的那些属性名。
4.注意:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。大部分情况,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如:在程序中需要创建某个类的几百万个实例对象。
关于__slots__的常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这种目的,但这不是它的使用初衷,更多是作为一个内存优化的工具。
1 classFoo: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 classFoo: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 classFoo:2 "描述信息"
3 pass
4 print(Foo.__doc__)5 #描述信息
doc
__module__和  __class__
__module__查看来自哪个模块
__class__查看来自哪个类


1 from aa.bb importFoo2 f1 =Foo()3 print(f1.__module__)4 print(f1.__class__)5 #ABCD
6 #aa.bb #模块信息
7 # #类信息
module class
__del__析构方法
当对象在内存中被释放时,自动触 发执行。
注:此方法一般无需定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交由Python解释器执行的,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。


1 classFoo:2 def __init__(self,name):3 self.name =name4 def __del__(self):5 print("del is running")6 f1=Foo("alex")7 delf18 print("=====>")9 #del is running #删除实例所以触发__del__
10 #=====> #执行完毕继续运行后面的程序
del
del只在实例被删除的时候被触发,而不是属性被删除就触发


1 classFoo:2 def __init__(self,name):3 self.name =name4 def __del__(self):5 print("del is running")6 f1=Foo("alex")7 delf1.name8 print("=====>")9 #=====> #没有先触发__del__而是先执行后面的程序
10 #del is running #整个程序执行完毕内存释放所以__del__运行
删除属性的效果
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名(); 对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()


1 classFoo:2 def __call__(self):3 print("ABCD")4 f1 = Foo() #创建了实例
5 f1() #Foo下的__call__
6 #ABCD
call
__next__ 和__iter__实现迭代器协议
从迭代器协议讲起


1 classFoo:2 def __init__(self,n):3 self.n =n4 def __iter__(self):5 returnself6 def __next__(self):7 self.n += 1
8 returnself.n9 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 classFib:2 def __init__(self):3 self._a = 1
4 self._b = 1
5
6 def __iter__(self):7 returnself8
9 def __next__(self):10 if self._a > 100:11 raise StopIteration("终止")12 self._a, self._b = self._b, self._a +self._b13 returnself._a14 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 inf1: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 classFoo:2 @property3 defAAA(self):4 print('get AAA')5 @AAA.setter6 defAAA(self,value):7 print("set AAA")8 @AAA.deleter9 defAAA(self):10 print("delete AAA")11
12 f1=Foo()13 f1.AAA='aaa'
14 delf1.AAA15 #set AAA
16 #delete AAA
这种效果也一样
1 classFoo:2 def get_AAA(self): #这几个函数的顺序不可改变
3 print('get AAA')4 defset_AAA(self,value):5 print("set AAA")6 defdel_AAA(self):7 print("delete AAA")8 AAA=property(get_AAA,set_AAA,del_AAA)9 f1=Foo()10 f1.AAA='aaa'
11 delf1.AAA12 #set AAA
13 #delete AAA
metaclass 元类
1、引子
Python中一切皆对象,类本身也是一个对象。当使用关键字class时,python解释器在加载class的时候就会创建出一个对象(这里的对象指的是类而非类的示例)
1 classFoo:2 pass
3 f1 =Foo()4 print(type(Foo))5 print(type(f1))6 # #类的类就是type
7 # #表示obj对象由Foo类创建
2、什么是元类
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样
元类的示例为类,正如类的实例为对象
type是python内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3、创建类的两种方式
1 classFoo:2 def __init__(self,name):3 self.name =name4 print(Foo)5 def __init__(self,name):6 self.name =name7 deftest(self):8 print("test------")9 FFo = type("FFo",(object,),{"x":1,"__init__":__init__,"test":test})10 print(FFo)11 #
12 #
13 f1 = FFo("alex")14 print(f1.x)15 #1
16 f1.test()17 #test------
4、一个类没有声明自己的元类,则默认其元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类


1 classMyType(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 returnobj12 class Foo(metaclass=MyType): #MyType("Foo",(object,),{})触发init
13 def __init__(self,name):14 self.name =name15 print(Foo)16 f1=Foo("alex")17 print(f1.__dict__)18 #我的元类
19 #Foo
20 #()
21 #{'__module__': '__main__', '__qualname__': 'Foo', '__init__': }
22 #
23 #====>
24 #{'name': 'alex'}
自定义元类MyType