文章目录
一、上下文管理协议
1、什么是上下文管理协议
上下文管理协议,即with语句。为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__()和__exit__()方法。
2、代码形式
with obj() as f:
‘代码块’
3、工作流程
(1)with obj() as f: f=obj.__enter__()
即:obj()是实例化,执行__init__(),再运行obj.__enter__(),赋值给f
(2)执行代码块
①没有异常的情况下,整个代码块运行完毕后去触发__exit__(),它的三个参数都为None。
②有异常的情况下,从异常出现的位置直接触发__exit__()
a:如果__exit__()的返回值为True,代表吞掉了异常
b:如果__exit__()的返回值不为True,代表吐出了异常
c:exit()的的运行完毕就代表了整个with语句的执行完毕
class Foo:
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)
return True
with Foo('a.txt') as f:
print(f)
print(asdfsaasdfasdfasdfasdfasfasdfasdfasdfasdfasfdasfd) #触发__exit__
print(f.name)
print('-----------------')
print('000000000000000000000000000000000000000000000')
4、用途
①使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预。
②在需要管理一些资源,比如文件、网络链接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制。
二、描述符
1、什么是描述符
描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()方法中的一个,这也被称为描述符协议。必须把描述符定义成这个类的类属性,不能为定义到构造函数中。
2、三个方法的触发
class Str:
def __get__(self, instance, owner):
print('Str调用', instance, owner)
def __set__(self, instance, value):
print('Str设置', instance, value)
def __delete__(self, instance):
print('Str删除', instance)
class People:
name = Str()
def __init__(self, name): # name被Str类代理
self.name = name
p1 = People('CRTao') # Str设置 <__main__.People object at 0x000001DA4153AF40> CRTao 触发__set__()方法
p1.name # Str调用 <__main__.People object at 0x000001DA4153AF40> <class '__main__.People'> 触发__get__()方法
p1.name = 'CRT' # Str设置 <__main__.People object at 0x000001DA4153AF40> CRT 触发__set__()方法
del p1.name # Str删除 <__main__.People object at 0x000001DA4153AF40> 触发__delete__()方法
print(p1.__dict__) # {}
3、描述符种类
①数据描述符:有__set__()或__delete__()方法
②非数据描述符:只定义了__get__()方法
4、描述符优先级
类属性>数据描述符
class Foo:
def __get__(self, instance, owner):
print('get方法')
def __set__(self, instance, value):
print('set方法',instance,value)
def __delete__(self, instance):
print('delete方法')
class Bar:
x=Foo()
print(Bar.x) # get方法 None
Bar.x=1 # 不触发Foo的__set__()方法
print(Bar.x) # 1 此时类属性x已经被修改,不是描述符Foo了
数据描述符>实例属性
class Foo:
def __get__(self, instance, owner):
print('get方法')
def __set__(self, instance, value):
print('set方法',instance,value)
def __delete__(self, instance):
print('delete方法')
class Bar:
x=Foo()
b1=Bar()
b1.x # get方法
b1.x=1 # set方法 <__main__.Bar object at 0x00000284787BAF10> 1
del b1.x # delete方法
实例属性>非数据描述符
非数据描述符>找不到属性,触发__getattr__()
class Foo:
def __get__(self, instance, owner):
print('===>get方法')
class Bar:
x=Foo()
def __getattr__(self, item):
print('触发getattr')
b1=Bar()
b1.x=1
print(b1.__dict__) # {'x': 1}
b1.xx # 触发getattr
print(b1.x) # 1
5、描述符小实验:实例化类型检验
class Typed:
def __init__(self,key,expect_type):
self.key = key
self.expect_type = expect_type
def __get__(self,instance,owner):
return instance.__dict__[self.key]
def __set__(self,instance,value):
if not isinstance(value,self.expect_type):
raise TypeError("传入的 %s 不是%s" %(self.key,self.expect_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
instance.__dict__.pop(self.key)
class People:
name = Typed("name",str)
age = Typed("age",int)
def __init__(self,name,age):
self.name = name
self.age = age
p1 = People("CRTao",18)
print(p1.__dict__) # {'name': 'CRTao', 'age': 18}
del p1.name
print(p1.__dict__) # {'age': 18}
三、类的装饰器
1、什么是类的装饰器
一切皆对象,实际上@deco,在对deco(obj)传实参的时候,只要是对象就行,并不检查是否为类或函数。
def deco(obj):
obj.x = 1
return obj
@deco #相当于 Foo=deco(Foo),
class Foo:
pass
print(Foo.x) # 1
2、类的装饰器小实验1:类型检验
class Typed:
def __init__(self,key,expect_type):
self.key = key
self.expect_type = expect_type
def __get__(self,instance,owner):
return instance.__dict__[self.key]
def __set__(self,instance,value):
if not isinstance(value,self.expect_type):
raise TypeError("传入的 %s 不是%s" %(self.key,self.expect_type))
instance.__dict__[self.key] = value
def __delete__(self, instance):
instance.__dict__.pop(self.key)
def deco(**kwargs):
def wrapper(obj):
for key,val in kwargs.items():
setattr(obj,key,Typed(key,val))
return obj
return wrapper
@deco(name=str,age=int)
class People:
def __init__(self,name,age):
self.name = name
self.age = age
p1 = People("CRTao",18)
print(p1.__dict__) # {'name': 'CRTao', 'age': 18}
del p1.name
print(p1.__dict__) # {'age': 18}
类一旦被定义后,__dict__会被冻结为只读状态,不能直接操作它。如:类.__dict__[key]=value会报错。可以直接访问属性并赋值,或者使用 setattr 函数来实现
3、类的装饰器小实验2:利用描述符自定制property
property效果
class Room:
def __init__(self, width, length):
self.width = width
self.length = length
@property
def area1(self):
return self.length * self.width
r1 = Room(10,5)
print(r1.area1) # 50
print(Room.area1) # <property object at 0x000001E40054F7C0>
print(Room.__dict__["area1"]) # <property object at 0x00000198EA1EF7C0>
模仿property效果,自定制LazyProperty,实现存储已计算结果功能
class LazyProperty:
def __init__(self,func):
self.func = func
def __get__(self,instance,owner):
print("get")
if not instance: # 类调用area,会返回None
return self
else:
res = self.func(instance)
setattr(instance,self.func.__name__,res)
return res
class Room:
def __init__(self, width, length):
self.width = width
self.length = length
@LazyProperty
def area(self):
return self.length * self.width
r1 = Room(10,5)
print(r1.area) # get 50
print(r1.area) # 50
print(Room.area) # <__main__.LazyProperty object at 0x000001861E70CD30>
4、类的装饰器小实验3:自定制类方法ClassMethod
class ClassMethod:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(*args,**kwargs):
print('在这里可以加功能啊')
return self.func(owner,*args,**kwargs)
return feedback
class People:
name='CRTao'
@ClassMethod # say=ClassMethod(say)
def say(cls,age):
print('%s is %s' %(cls.name,age))
People.say(18)
'''
在这里可以加功能啊
CRTao is 18
'''
p1=People()
p1.say(180)
'''
在这里可以加功能啊
CRTao is 180
'''
四、property补充知识
1、静态属性的增删改查
一个静态属性property本质就是实现了__get__、__set__、__delete__方法,其为数据描述符。
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')
@AAA.setter
def AAA(self,val):
print('set的时候运行我啊',val)
@AAA.deleter
def AAA(self):
print('del的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
#定义AAA.setter,AAA.deleter后,可以执行倒数两行代码,否则报错
f1=Foo()
f1.AAA # get的时候运行我啊
f1.AAA='aaa' # set的时候运行我啊 aaa
del f1.AAA # del的时候运行我啊
下述代码效果同上:
class Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,val):
print('set的时候运行我啊',val)
def del_AAA(self):
print('del的时候运行我啊')
AAA=property(get_AAA,set_AAA,del_AAA)
f1=Foo()
f1.AAA # get的时候运行我啊
f1.AAA='aaa' # set的时候运行我啊 aaa
del f1.AAA # del的时候运行我啊
2、小实验:用property实现类型检验
class People:
def __init__(self,name):
self.name=name #实例化就触发property
@property
def name(self):
return self.nm
@name.setter
def name(self,value):
if not isinstance(value,str):
raise TypeError('必须是字符串类型')
self.nm=value
@name.deleter
def name(self):
del self.nm
p1=People('CRTao') #self.name实际是存放到self.nm里
print(p1.name) # CRTao
del p1.name
print(p1.__dict__) # {}
# p1.name=1
五、元类
1、什么是元类
类的类,即类的模版。元类的实例是类。
type是Pthon的一个内建元类。pthon中的任何class定义的类,默认都是type的实例化对象。
2、创建类的两种方法
除了使用默认的元类type,用户也可以通过继承type来自定义元类:
type(类名,父类,类的属性字典)
注:由于父类可能是多个,所以要写成元组形式
#方法一
class Foo1:
x =1
def func1(self):
pass
#方法二
def func2(self):
pass
Foo2 = type("Foo2",(object,),{"func2":func2,"y":2})
3、自定制元类
class MyType(type):
def __init__(self,a,b,c):
print(self) # <class '__main__.Foo'>
print("a:%s,b:%s,c:%s" %(a,b,c))
# a:Foo,b:(),c:{'__module__': '__main__', '__qualname__': 'Foo', '__init__': <function Foo.__init__ at 0x000001EA705CE280>}
def __call__(self, *args, **kwargs):
print("self:%s,args:%s,kwargs:%s" %(self,args,kwargs))
obj=object.__new__(self) #object.__new__(Foo)--> 产生f1,赋给obj
self.__init__(obj,*args,**kwargs) #Foo.__init__(f1,*arg,**kwargs)
return obj
class Foo(metaclass=MyType):
'''
metaclass=MyType 相当于执行 Foo=MyType(Foo,'Foo',(),{}),
触发MyType的__init__方法
'''
def __init__(self,name):
self.name=name # f1.name=name
f1=Foo('CRTao') # 触发MyType的__call__方法
print(f1) # <__main__.Foo object at 0x00000201E071BFD0>
print(f1.__dict__) # {'name': 'CRTao'}