当前位置: 首页 > 后端技术 > Python

说说Python的元编程

时间:2023-03-26 16:29:49 Python

提到元这个词,你可能会想到元数据。元数据是描述数据本身的数据。元类是类的类。相应的元编程是描述代码本身的代码。编程就是创建操作源代码的函数和类,例如修改、生成或包装原始代码。主要技术是使用装饰器、元类、描述符类。本文的主要目的是介绍这些元编程技术,并举例说明它们如何自定义源代码的行为。装饰器装饰器是函数的函数,它接受一个函数作为参数并返回一个新的函数,在不改变原函数代码的情况下向其添加新的函数,比如最常用的时序装饰器:fromfunctoolsimportwrapsdeftimeit(logger=None):"""耗时统计装饰器,单位秒,保留小数点后4位"""defdecorator(func):@wraps(func)defwrapper(*args,**kwargs):开始=time.time()结果=func(*args,**kwargs)end=time.time()iflogger:logger.info(f"{func.__name__}cost{end-start:.4f}seconds")else:print(f"{func.__name__}cost{end-start:.4f}seconds")returnresultreturnwrapperreturndecorator(注意:比如上面的@wraps(func)注解很重要,它可以保留原来的Function元数据)只要在原来的函数上加上@timeit()就可以增加新的函数:@timeit()deftest_timeit():time.sleep(1)test_timeit()#test_timeitcost1.0026seconds以上e代码等同于:test_timeit=timeit(test_timeit)test_timeit()装饰器的执行顺序当有多个装饰器时,它们的调用顺序是什么?如果有这样一段代码,我应该先打印Decorator1还是Decorator2呢?从functoolsimportwrapsdefdecorator1(func):@wraps(func)defwrapper(*args,**kwargs):print('Decorator1')returnfunc(*args,**kwargs)returnwrapperdefdecorator2(func):@wraps(func)defwrapper(*args,**kwargs):print('装饰器2')returnfunc(*args,**kwargs)returnwrapper@decorator1@decorator2defadd(x,y):returnx+yadd(1,2)#Decorator1#Decorator2在回答这个问题之前,先给大家打个形象比喻。装饰器就像一个穿衣服的函数。例子中decorator1是上衣,decorator2是内衣add=decorator1(decorator2(add))调用函数时,就像脱衣服一样,先把最外面的decorator1去掉,即先打印Decorator1,然后执行returnfunc(args,kwargs)会移除decorator2,然后打印Decorator2,当再次执行returnfunc(args,kwargs)时,add()函数才会真正执行。需要注意的是打印位置,如果打印字符串的代码位于调用函数之后,像下面这样,输出正好相反:defdecorator1(func):@wraps(func)defwrapper(*args,**kwargs):result=func(*args,**kwargs)print('Decorator1')返回结果returnwrapperdefdecorator2(func):@wraps(func)defwrapper(*args,**kwargs):result=func(*args,**kwargs)print('Decorator2')returnresultreturnwrapper装饰器不仅可以定义为函数,也可以定义为类,只要确保它实现了__call__()和__get__()方法。元类Python中所有类(对象)的元类是类型类,也就是说Python类的创建行为由默认的类型类控制。打个比方,类型类是所有类的祖先。我们可以通过编程方式实现一些自定义对象创建行为。指定一个类继承类型类A,然后让其他类的元类指向A,就可以控制A的创建行为。通常,单例是使用元类实现的:self,*args,**kwargs):如果self._instance为None:self._instance=super().__call__(*args,**kwargs)returnself._instanceelse:returnself._instanceclassSpam(metaclass=Singleton):def__init__(self):print("Spam!!!")元类Singleton的__init__和__new__方法会在Spam定义时执行,__call__方法会在Spam实例化时执行。描述符类(descriptorclass)描述符是任何定义了__get__()、__set__()或__delete__()的对象。描述符允许对象自定义属性查找、存储和删除操作。下面是官方文档[1]中的一个自定义验证器的例子。定义一个验证器类,它是一个描述符类和一个抽象类:fromabcimportABC,abstractmethodclassValidator(ABC):def__set_name__(self,owner,name):self.private_name='_'+namedef__get__(self,obj,objtype=None):returngetattr(obj,self.private_name)def__set__(self,obj,value):self.validate(value)setattr(obj,self.private_name,value)@abstractmethoddefvalidate(self,value):pass自定义验证器需要继承Validator,必须提供validate()方法来根据需要测试各种约束。以下是三个有用的数据验证工具:OneOf验证一个值是一组受限选项中的一个。classOneOf(Validator):def__init__(self,*options):self.options=set(options)defvalidate(self,value):ifvaluenotinself.options:raiseValueError(f'Expected{value!r}是{self.options!r}')Number之一验证值是整数还是浮点数。根据可选参数,它还可以验证该值是否介于给定的最小值或最大值之间。类号(验证器):def__init__(自我,最小值=无,最大值=无):自我。最小值=最小值自我。maxvalue=maxvaluedefvalidate(self,value):ifnotisinstance(value,(int,float)):raiseTypeError(f'Expected{value!r}tobeanintorfloat')如果self.minvalue不是Noneandvalueself.maxvalue:raiseValueError(f'预期{value!r}不超过{self.maxvalue!r}')String验证值是str。根据可选参数,它可以验证给定的最小或最大长度。它还可以验证用户定义的谓词。classString(Validator):def__init__(self,minsize=None,maxsize=None,predicate=None):self.minsize=minsizeself.maxsize=maxsizeself.predicate=predicatedefvalidate(self,value):如果不是isinstance(value,str):如果self.minsize不是None并且len(value)self.maxsize:raiseValueError(f'Expected{value!r}不大于{self.maxsize!r}')ifself.predicateisnotNoneandnotself.predicate(value):raiseValueError(f'Expected{self.predicate}tobetruefor{value!r}')实际应用时这样写:classComponent:name=String(minsize=3,maxsize=10,predicate=str.isupper)kind=OneOf('wood','metal','plastic')数量=Number(minvalue=0)def__init__(self,name,kind,quantity):self.name=nameself.kind=kindself.quantity=数量描述器阻止无效实例的创建:>>>Component('Widget','metal',5)#Blocked:'Widget'isnotalluppercaseTraceback(mostrecentcalllast):...ValueError:Expected对'Widget'为真>>>Component('WIDGET','metle',5)#Blocked:'metle'ismisspelledTraceback(mostrecentcalllast):...ValueError:预期'metle'是{'metal','plastic','wood'}之一}>>>Component('WIDGET','metal',-5)#Blocked:-5isnegativeTraceback(最近的调用最后):...ValueError:Expected-5tobeatleast0>>>Component('WIDGET','metal','V')#Blocked:'V'isn'tanumberTraceback(mostrecentcalllast):...TypeError:预期“V”为int或float>>>c=Component('WIDGET','metal',5)#Allowed:Theinputsarevalid关于Python元编程的最后一句话总结如下:如果你希望某些功能具有相同的编写重复代码,易于维护,你可以使用装饰器实现如果你想让一些类具有一些相同的特性,或者在类定义中控制它们,我们可以自定义一个元类,然后让它的类的元类指向那个类。如果希望实例的属性具有一些共同的特征,可以自定义一个描述符类。最近为初学者整理了数百G的Python学习资料,包括电子书、教程、源码等,免费分享给大家!想上“Python编程学习圈”,发“J”免费领取