python实现装饰器、描述符
概要
本人python理论知识远达不到传授级别,写文章主要目的是自我总结,并不能照顾所有人,请见谅,文章结尾贴有相关链接可以作为补充
全文分为三个部分装饰器理论知识、装饰器应用、装饰器延申
- 装饰理基础:无参装饰器、有参装饰器、functiontools、装饰器链
- 装饰器进阶:property、staticmethod、classmethod源码分析(python代码实现)
装饰器基础
无参装饰器
'''
假定有一个需求是:打印程序函数运行顺序
此案例打印的结果为:
foo1functionisstarting
foo2functionisstarting
'''
fromfunctoolsimportwraps
defNoParamDec(func):
#函数在被装饰器装时后,其函数属性也会改变,wraps作用就是保证被装饰函数属性不变
@wraps(func)
defwarpper(*args,**kwargs):
print('{}functionisstarting'.format(func.__name__))
returnfunc(*args,**kwargs)
returnwarpper
#python黑魔法省略了NoParamDec=NoParamDec(foo1)
@NoParamDec
deffoo1():
foo2()
@NoParamDec
deffoo2():
pass
if__name__=="__main__":
foo1()
有参装饰器
'''
假定有一个需求是:检查函数参数的类型,只允许匹配正确的函数通过程序
此案例打印结果为:
('a','b','c')
-----------------------分割线------------------------
ERROS!!!!bmustbe
ERROS!!!!cmustbe
('a',2,['b','d'])
'''
fromfunctoolsimportwraps
frominspectimportsignature
deftypeAssert(*args,**kwargs):
deco_args=args
deco_kwargs=kwargs
deffactor(func):
#python标准模块类,可以用来检查函数参数类型,只允许特定类型通过
sig=signature(func)
#将函数形式参数和规定类型进行绑定
check_bind_args=sig.bind_partial(*deco_args,**deco_kwargs).arguments
@wraps(func)
defwrapper(*args,**kwargs):
#将实际参数值和形式参数进行绑定
wrapper_bind_args=sig.bind(*args,**kwargs).arguments.items()
forname,objinwrapper_bind_args:
#遍历判断是否实际参数值是规定参数的实例
ifnotisinstance(obj,check_bind_args[name]):
try:
raiseTypeError('ERROS!!!!{arg}mustbe{obj}'.format(**{'arg':name,'obj':check_bind_args[name]}))
exceptExceptionase:
print(e)
returnfunc(*args,**kwargs)
returnwrapper
returnfactor
@typeAssert(str,str,str)
definspect_type(a,b,c):
return(a,b,c)
if__name__=="__main__":
print(inspect_type('a','b','c'))
print('{:-^50}'.format('分割线'))
print(inspect_type('a',2,['b','d']))
装饰器链
'''
假定有一个需求是:
输入类似代码:
@makebold
@makeitalic
defsay():
return"Hello"
输出:
Hello
'''
fromfunctoolsimportwraps
defhtml_deco(tag):
defdecorator(fn):
@wraps(fn)
defwrapped(*args,**kwargs):
return'<{tag}>{fn_result}<{tag}>'.format(**{'tag':tag,'fn_result':fn(*args,**kwargs)})
returnwrapped
returndecorator
@html_deco('b')
@html_deco('i')
defgreet(whom=''):
#等价于geet=html_deco('b')(html_deco('i)(geet))
return'Hello'+(''+whom)ifwhomelse''
if__name__=="__main__":
print(greet('world'))#->Helloworld
装饰器进阶
property原理
通常,描述符是具有“绑定行为”的对象属性,其属性访问已经被描述符协议中的方法覆盖。这些方法是__get__()、__set__()和__delete__()。如果一个对象定义这些方法中的任何一个,它被称为一个描述符。如果对象定义__get__()和__set__(),则它被认为是数据描述符。仅定义__get__()的描述器称为非数据描述符(它们通常用于方法,但是其他用途也是可能的)。
属性查找优先级为:
- 类属性
- 数据描述符
- 实例属性
- 非数据描述符
- 默认为__getattr__()
classProperty(object):
'''
内部property是用c实现的,这里用python模拟实现property功能
代码参考官方doc文档
'''
def__init__(self,fget=None,fset=None,fdel=None,doc=None):
self.fget=fget
self.fset=fset
self.fdel=fdel
self.__doc__=doc
def__get__(self,obj,objtype=None):
ifobjisNone:
returnself
ifself.fgetisNone:
raise(AttributeError,"unreadableattribute")
print('self={},obj={},objtype={}'.format(self,obj,objtype))
returnself.fget(obj)
def__set__(self,obj,value):
ifself.fsetisNone:
raise(AttributeError,"can'tsetattribute")
self.fset(obj,value)
def__delete__(self,obj):
ifself.fdelisNone:
raise(AttributeError,"can'tdeleteattribute")
self.fdel(obj)
defgetter(self,fget):
returntype(self)(fget,self.fset,self.fdel,self.__doc__)
defsetter(self,fset):
returntype(self)(self.fget,fset,self.fdel,self.__doc__)
defdeleter(self,fdel):
returntype(self)(self.fget,self.fset,fdel,self.__doc__)
classStudent(object):
@Property
defscore(self):
returnself._score
@score.setter
defscore(self,val):
ifnotisinstance(val,int):
raiseValueError('scoremustbeaninteger!')
ifval>100orval<0:
raiseValueError('scoremustbetween0~100!')
self._score=val
if__name__=="__main__":
s=Student()
s.score=60
s.score
staticmethod原理
@staticmethodmeans:whenthismethodiscalled,wedon'tpassaninstanceoftheclasstoit(aswenormallydowithmethods).Thismeansyoucanputafunctioninsideaclassbutyoucan'taccesstheinstanceofthatclass(thisisusefulwhenyourmethoddoesnotusetheinstance).
classStaticMethod(object):
"python代码实现staticmethod原理"
def__init__(self,f):
self.f=f
def__get__(self,obj,objtype=None):
returnself.f
classE(object):
#StaticMethod=StaticMethod(f)
@StaticMethod
deff(x):
returnx
if__name__=="__main__":
print(E.f('staticMethodTest'))
classmethod
@staticmethodmeans:whenthismethodiscalled,wedon'tpassaninstanceoftheclasstoit(aswenormallydowithmethods).Thismeansyoucanputafunctioninsideaclassbutyoucan'taccesstheinstanceofthatclass(thisisusefulwhenyourmethoddoesnotusetheinstance).
classClassMethod(object):
"python代码实现classmethod原理"
def__init__(self,f):
self.f=f
def__get__(self,obj,klass=None):
ifklassisNone:
klass=type(obj)
defnewfunc(*args):
returnself.f(klass,*args)
returnnewfunc
classE(object):
#ClassMethod=ClassMethod(f)
@ClassMethod
deff(cls,x):
returnx
if__name__=="__main__":
print(E().f('classMethodTest'))