提到元这个词,你可能会想到元数据。元数据是描述数据本身的数据。元类是类的类。相应的元编程是描述代码本身的代码。编程就是创建操作源代码的函数和类,例如修改、生成或包装原始代码。主要技术是使用装饰器、元类、描述符类。本文的主要目的是介绍这些元编程技术,并举例说明它们如何自定义源代码的行为。装饰器装饰器是函数的函数,它接受一个函数作为参数并返回一个新的函数,在不改变原有函数代码的情况下为其添加新的功能,比如最常用的时序装饰器:fromfunctoolsimportwrapsdeftimeit(logger=None):"""装饰器耗时统计,单位秒,保留4位小数"""defdecorator(func):@wraps(func)defwrapper(*args,**kwargs):start=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)注解很重要,它可以保留原函数的元数据)只需要在原有函数上添加@timeit()可以为其添加新的函数:@timeit()deftest_timeit():time.sleep(1)test_timeit()#test_timeitcost1.0026seconds上面代码效果相同ct如下:test_timeit=timeit(test_timeit)test_timeit()装饰器的执行顺序当有多个装饰器时,它们的调用顺序是什么?如果有这样一段代码,我应该先打印Decorator1还是Decorator2呢?fromfunctoolsimportwrapsdefdecorator1(func):@wraps(func)defwrapper(*args,**kwargs):print('Decorator1')returnfunc(*args,**kwargs)returnwrapperdefdecorator2(func):@wraps(func)defwrapper(*args)我先给大家打个形象比喻。装饰器就像一个穿衣服的函数。离它最近的先戴,远的最后戴。上面的例子中,decorator1是外套,decorator2是内衣。add=decorator1(decorator2(add))调用函数时,就像脱衣服一样。先去掉最外层的decorator1,即先打印Decorator1。执行returnfunc(*args,**kwargs)时,会移除decorator2,然后打印decorator2。add()函数将在returnfunc(*args,**kwargs)执行时实际执行。需要注意的是印刷位置。如果打印字符串的代码位于调用函数之后,如下所示,输出正好相反: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的创建行为。典型的实现是使用元类实现单例:classSingleton(type):def__init__(self,*args,**kwargs):self._instance=Nonesuper().__init__(*args,**kwargs)def__call__(self,*args,**kwargs):ifself._instanceisNone:self._instance=super().__call__(*args,**kwargs)returnself._instanceelse:returnself._instanceclassSpam(metaclass=Singleton):def__init__(self):print("Spam!!!")元类Singleton的__init__和__new__方法将在执行Spam的定义,__call__方法将在Spam实例化时执行。如果你想更好地理解元类,可以阅读Python黑魔法的metaclassdescriptor类(描述符类)。描述符是定义__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):passcustomvalidatorrequiredInheritsfromValidatorand必须提供一个validate()方法来根据需要测试各种约束。以下是三个有用的数据验证工具:OneOf验证一个值是一组受限选项中的一个。classOneOf(Validator):def__init__(self,*options):self.options=set(options)defvalidate(self,value):ifvaluenotinself.options:raiseValueError(f'Expected{value!r}tobeoneof{self.options!r}')Number验证值是整数还是浮点数。根据可选参数,它还可以验证该值是否介于给定的最小值或最大值之间。classNumber(Validator):def__init__(self,minvalue=None,maxvalue=None):self.minvalue=minvalueself.maxvalue=maxvaluedefvalidate(self,value):ifnotisinstance(value,(int,float)):raiseTypeError(f'Expected{value!r}tobeanintorfloat')ifself.minvalueisnotNoneandvalue
