python(描述符应用与类的装饰器)
Posted hcy12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python(描述符应用与类的装饰器)相关的知识,希望对你有一定的参考价值。
__enter__和__exit__
数据描述符:至少实现__get__,__set__方法的
非数据描述符:没有__set__方法的
上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') # return self def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') with Open(\'b.txt\') as f: print(\'=====>执行代码块\') # print(f,f.name)
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行 print(exc_type) #异常类型 print(exc_val) #异常值 print(exc_tb) #追溯信息 with Open(\'b.txt\') as f: #触发__enter__ print(\'=====>执行代码块\') raise AttributeError(\'***着火啦,救火啊***\') print(\'0\'*100) #------->不会执行
用途:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制
描述符应用
控制输入类型
# -*- coding: utf-8 -*- class Typed: def __init__(self,key): self.key=key def __get__(self,instance,owner): print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,str): print(\'输入不是字符串\') else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\') # age=Typed(\'age\') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000)
进一步改进
# -*- coding: utf-8 -*- class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self,instance,owner): # print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): # print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,self.expected_type): print(\'输入不是%s\'%self.expected_type) else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\',str) age=Typed(\'age\',int) salary=Typed(\'salary\',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000.0) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000.0)
类的装饰器
初级(没有参数):
# -*- coding: utf-8 -*- def decorate(cls): print(\'类的装饰器开始运行啦------>\') return cls @decorate #无参:People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(self.age,self.name,self.salary) p1=People(\'egon\',18,3333.3)
------------恢复内容开始------------
__enter__和__exit__
数据描述符:至少实现__get__,__set__方法的
非数据描述符:没有__set__方法的
上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') # return self def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') with Open(\'b.txt\') as f: print(\'=====>执行代码块\') # print(f,f.name)
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行 print(exc_type) #异常类型 print(exc_val) #异常值 print(exc_tb) #追溯信息 with Open(\'b.txt\') as f: #触发__enter__ print(\'=====>执行代码块\') raise AttributeError(\'***着火啦,救火啊***\') print(\'0\'*100) #------->不会执行
用途:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制
描述符应用
控制输入类型
# -*- coding: utf-8 -*- class Typed: def __init__(self,key): self.key=key def __get__(self,instance,owner): print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,str): print(\'输入不是字符串\') else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\') # age=Typed(\'age\') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000)
进一步改进
# -*- coding: utf-8 -*- class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self,instance,owner): # print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): # print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,self.expected_type): print(\'输入不是%s\'%self.expected_type) else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\',str) age=Typed(\'age\',int) salary=Typed(\'salary\',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000.0) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000.0)
类的装饰器
初级(没有参数):
# -*- coding: utf-8 -*- def decorate(cls): print(\'类的装饰器开始运行啦------>\') return cls @decorate #无参:People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(self.age,self.name,self.salary) p1=People(\'egon\',18,3333.3)
------------恢复内容开始------------
__enter__和__exit__
数据描述符:至少实现__get__,__set__方法的
非数据描述符:没有__set__方法的
上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') # return self def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') with Open(\'b.txt\') as f: print(\'=====>执行代码块\') # print(f,f.name)
# -*- coding: utf-8 -*- class Open: def __init__(self,name): self.name=name def __enter__(self): print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\') def __exit__(self, exc_type, exc_val, exc_tb): print(\'with中代码块执行完毕时执行我啊\') #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行 print(exc_type) #异常类型 print(exc_val) #异常值 print(exc_tb) #追溯信息 with Open(\'b.txt\') as f: #触发__enter__ print(\'=====>执行代码块\') raise AttributeError(\'***着火啦,救火啊***\') print(\'0\'*100) #------->不会执行
用途:
1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制
描述符应用
控制输入类型
# -*- coding: utf-8 -*- class Typed: def __init__(self,key): self.key=key def __get__(self,instance,owner): print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,str): print(\'输入不是字符串\') else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\') # age=Typed(\'age\') def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000)
进一步改进
# -*- coding: utf-8 -*- class Typed: def __init__(self,key,expected_type): self.key=key self.expected_type=expected_type def __get__(self,instance,owner): # print(\'get方法\') # print(\'isinstance参数;\',isinstance) # print(\'owner参数;\',owner) return instance.__dict__[self.key] def __set__(self,instance,value): # print(\'set方法\') # print(\'isinstance参数;\',instance) # print(\'owner参数;\',value) #通过初始化函数写活了 if not isinstance(value,self.expected_type): print(\'输入不是%s\'%self.expected_type) else: instance.__dict__[self.key]=value def __delete__(self,instance): print(\'delete方法\') # print(\'isinstance参数;\', instance) instance.__dict__.pop(self.key) class people: name=Typed(\'name\',str) age=Typed(\'age\',int) salary=Typed(\'salary\',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=people("bob",22,5000.0) p1.name print(p1.__dict__) #没有name属性,他被数据描述符代理了 del p1.name print(p1.__dict__) p2=people(1,22,5000.0)
类的装饰器
初级(没有参数):
# -*- coding: utf-8 -*- def decorate(cls): print(\'类的装饰器开始运行啦------>\') return cls @decorate #无参:People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(self.age,self.name,self.salary) p1=People(\'egon\',18,3333.3)
进阶(有参数)
# -*- coding: utf-8 -*- def typeassert(**kwargs): def decorate(cls): print(\'2------>\') for key,value in kwargs.items(): setattr(cls,key,value) return cls print(\'1------>\', kwargs) return decorate @typeassert() #name=str,age=int,salary=float #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) class People: pass # def __init__(self,name,age,salary): # self.name=name # self.age=age # self.salary=salary print(People.__dict__) @typeassert(hobby=\'ball\') class bar(): pass
#添加了hobby属性 print(bar.__dict__)
可添加不定个数属性的示例:
# -*- coding: utf-8 -*- class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print(\'get--->\',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print(\'set--->\',instance,value) if not isinstance(value,self.expected_type): raise TypeError(\'Expected %s\' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print(\'delete--->\',instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print(\'类的装饰器开始运行啦------>\',kwargs) for name,expected_type in kwargs.items(): setattr(cls,name,Typed(name,expected_type)) return cls return decorate @typeassert(name=str,age=int,salary=float)#在此添加要增加的属性 #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary print(People.__dict__) p1=People(\'egon\',18,3333.3)
自定制property
装饰器可以是函数,也可以是类类型。
# -*- coding: utf-8 -*- class Lazyproperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): print(\'这是我们自己定制的静态属性,r1.area实际是要执行r1.area()\') if instance is None: return self return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情 class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符 def area(self): return self.width * self.length r1=Room(\'alex\',1,1) print(r1.area) #将r1.area传参数写在Lazyproperty中的__get__里
应用:自制property实现延迟计算功能
# -*- coding: utf-8 -*- class Lazyproperty: def __init__(self,func): self.func=func def __get__(self, instance, owner): print(\'这是我们自己定制的静态属性,r1.area实际是要执行r1.area()\') if instance is None: return self else: print(\'--->\') value=self.func(instance) setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中 return value # 如果加了set,将该非数据描述符变为数据描述符,优先级高于实列属性,缓存能力丧失 # def __set__(self, instance, value): # print(\'hahahahahah\') class Room: def __init__(self,name,width,length): self.name=name self.width=width self.length=length @Lazyproperty #area=Lazyproperty(area) 相当于\'定义了一个类属性,即描述符\' def area(self): return self.width * self.length r1=Room(\'alex\',1,1) print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
property补充:一个静态属性property本质就是实现了get,set,delete三种方法
# -*- coding: utf-8 -*- class Foo: @property def AAA(self): print(\'get的时候运行我啊\') @AAA.setter def AAA(self,value): print(\'set的时候运行我啊\') @AAA.deleter def AAA(self): print(\'delete的时候运行我啊\') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA f1.AAA=\'aaa\' del f1.AAA
应用(类型检测)
# -*- coding: utf-8 -*- class People: def __init__(self,name): self.name=name #实例化就触发property @property def name(self): # return self.name #无限递归 print(\'get------>\') return self.DouNiWan @name.setter def name(self,value): print(\'set------>\') if not isinstance(value,str): raise TypeError(\'必须是字符串类型\') self.DouNiWan=value @name.deleter def name(self): print(\'delete------>\') del self.DouNiWan p1=People(\'alex\') #self.name实际是存放到self.DouNiWan里 print(p1.name) print(p1.__dict__) p1.name=1
元类
所有定义的类都是由type产生的。
# -*- coding: utf-8 -*- class foo: pass print(type(foo))
定义类的两种方法
# -*- coding: utf-8 -*- class foo: pass print(type(foo)) print(foo) def __init__(self,name,age): self.name=name self.age=age def test(self): print(\'=======>\') Foo=type(\'foo1\',(object,),{\'x\':1,\'__init__\':__init__,\'test\':test}) print(Foo) print(Foo.__dict__) foo_1=Foo(\'alex\',22) foo_1.test()
自定义元类(详情查看连接)
以上是关于python(描述符应用与类的装饰器)的主要内容,如果未能解决你的问题,请参考以下文章