python的元类实用程序

Py-Meta-Utils的Python项目详细描述


py元实用程序

有用链接

作为库的元选项工厂模式,以及相关的元类实用程序

好的,但是元选项工厂模式是什么?也许最简单的解释方法是从一个例子开始。假设您希望您的最终用户能够有选择地从您正在编写的库中记录类的操作:

classEndUserClass(YourLoggableService):classMeta:debug:bool=Trueverbosity:int=2log_destination:str='/tmp/end-user-class.log'

第一步是定义自定义的MetaOption子类:

  • 实现所绝对需要的是构造函数及其name参数。也就是说,为了显式起见,建议还指定defaultinherit参数。
  • check_value方法是可选的,但对于确保用户不会给您垃圾很有用。
  • get_value方法有一个默认实现,通常不需要重写,除非默认值是可变的或具有高级逻辑。
  • 还有一个contribute_to_class方法,我们稍后将讨论。
importosimportsys# first we have to import what we need from py_meta_utilsfrompy_meta_utilsimport(McsArgs,MetaOption,MetaOptionsFactory,process_factory_meta_options,_missing)# then we have to declare the meta options the meta options factory should supportclassDebugMetaOption(MetaOption):def__init__(self):super().__init__(name='debug',default=False,inherit=True)defcheck_value(self,value,mcs_args:McsArgs):ifnotisinstance(value,bool):raiseTypeError(f'The {self.name} Meta option must be a bool')classVerbosityMetaOption(MetaOption):def__init__(self):super().__init__(name='verbosity',default=1,inherit=True)defcheck_value(self,value,mcs_args:McsArgs):ifvaluenotin{1,2,3}:raiseValueError(f'The {self.name} Meta option must either 1, 2, or 3')classLogDestinationMetaOption(MetaOption):def__init__(self):super().__init__(name='log_destination',default=_missing,inherit=True)# this pattern is useful if you need a mutable default value like [] or {}defget_value(self,Meta,base_classes_meta,mcs_args:McsArgs):value=super().get_value(Meta,base_classes_meta,mcs_args)returnvalueifvalue!=_missingelse'stdout'defcheck_value(self,value,mcs_args:McsArgs):ifvaluein{'stdout','stderr'}:returntry:valid_dir=os.path.exists(os.path.dirname(value))exceptException:valid_dir=Falseifnotvalid_dir:raiseValueError(f'The {self.name} Meta option must be one of `stdout`, ''`stderr`, or a valid filepath')

下一步是对MetaOptionsFactory进行子类划分,并指定所需的MetaOption子类:

classLoggingMetaOptionsFactory(MetaOptionsFactory):_options=[DebugMetaOption,VerbosityMetaOption,LogDestinationMetaOption,]

然后您需要一个元类来实际应用工厂选项:

classLoggingMetaclass(type):def__new__(mcs,name,bases,clsdict):mcs_args=McsArgs(mcs,name,bases,clsdict)factory_cls=mcs_args.getattr('_meta_options_factory_class',LoggingMetaOptionsFactory)options_factory=factory_cls()options_factory._contribute_to_class(mcs_args)# the above three lines can be replaced by:# process_factory_meta_options(mcs_args, LoggingMetaOptionsFactory)returnsuper().__new__(*mcs_args)

最后,使用刚刚定义的元类创建公共类:

classYourLoggableService(metaclass=LoggingMetaclass):defdo_important_stuff(self):ifself.Meta.verbosity<3:self._log('doing important stuff')else:self._log('doing really detailed important stuff like so')def_log(self,msg):ifnotself.Meta.debug:returnifself.Meta.log_destination=='stdout':print(msg)elifself.Meta.log_destination=='stderr':sys.stderr.write(msg)sys.stderr.flush()else:withopen(self.Meta.log_destination,'a')asf:f.write(msg)

选项工厂自动将Meta属性添加到正在构造的类(在本例中为YourLoggableService)。(在这种情况下,Meta属性将填充由工厂指定的MetaOption子类提供的默认值。)如果正在构造的类具有部分Meta类,则将缺少的元选项添加到该类中。(*)

(*)实际上就是这样,出于所有实际目的,可能是您应该如何考虑它,但从技术上讲,正在构造的类的Meta属性实际上被指定的MetaOptionsFactory子类的填充实例替换。

我们没有涉及的一件事是MetaOption.contribute_to_class。这是一个可选的回调挂钩,它允许MetaOption子类为正在构造的类贡献一些东西。它很可能向类添加/从类中删除属性,或者可能用decorator或其他东西包装某些方法。

在包含的源代码中可以找到一个很好的简单示例AbstractMetaOption

ABSTRACT_ATTR='__abstract__'classAbstractMetaOption(MetaOption):def__init__(self):super().__init__(name='abstract',default=False,inherit=False)defget_value(self,Meta,base_classes_meta,mcs_args:McsArgs):# class attributes take precedence over the class Meta's valueifmcs_args.clsdict.get(ABSTRACT_ATTR,False)isTrue:returnTruereturnsuper().get_value(Meta,base_classes_meta,mcs_args)isTruedefcontribute_to_class(self,mcs_args:McsArgs,value):ifvalueisTrue:mcs_args.clsdict[ABSTRACT_ATTR]=Trueelse:mcs_args.clsdict[ABSTRACT_ATTR]=False

许多库使用__abstract__类属性来确定构造中的类是否应该被视为具体的,但它们不理解类Meta选项。因此,我们实现了MetaOption.contribute_to_class,将__abstract__类属性设置为与此类库向后兼容的适当值。

包括元类实用程序

单重态

Singleton是一个包含的元类,它使任何使用它的类都成为一个单独的类:

frompy_meta_utilsimportSingletonclassYourSingleton(metaclass=Singleton):passinstance=YourSingleton()assertinstance==YourSingleton()

使用Singleton的类可以是子类,但是,必须通知子类的基类:

frompy_meta_utilsimportSingletonclassBaseSingleton(metaclass=Singleton):passclassExtended(BaseSingleton):passBaseSingleton.set_singleton_class(Extended)base_instance=BaseSingleton()extended_instance=Extended()assertbase_instance==extended_instance==BaseSingleton()==Extended()

深部皮肤

deep_getattr(clsdict,bases,'attr_name',[default])

deep_getattr的作用与构造类对象上的getattr一样,只是它在类前构造类字典和基类上操作。换句话说,我们首先在类字典中查找属性,然后搜索所有基类(按方法解析顺序),如果在类字典或基类中找不到属性,则返回默认值(如果未给定属性,则返回AttributeError)。

optionalmaclass和optionalclass

try:fromoptional_dependencyimportSomeClassexceptImportError:frompy_meta_utilsimportOptionalClassasSomeClassclassOptional(SomeClass):pass

许可证

麻省理工学院

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java用变化的替换字符串替换子字符串   从数据库中断中恢复的oracle Java DAL?   Android/Java页边距位于左/右/底部   java如何用相同的源代码构建不同的APK?(我发现了一个错误)   java正则表达式,仅当字符串以一行中的3个数字开头时才匹配第一个数字   使用以xml为输入的给定端点调用java中的rest-ful web服务?   java长字符串转换为UTF8引发异常   java如何使用截取方法获取ArrayList   java将计算列添加到可观察列表中   正则表达式如何在java正则表达式中使用组?   java正则表达式只接受字母表和空格,不允许在字符串的开头和结尾使用空格   java简单onclick按钮在安卓中不起作用   java如何在Spring中只实现Crudepository的特定方法?   java无法使用json对象NPE读取jsonarray   java我可以添加maven依赖项,这些依赖项被打包为除此之外的任何东西。罐子