Python 使用基于装饰器的方法注册表初始化
我有一个类,它有几个方法,每个方法都有特定的属性(从质量上讲)。我希望这些方法在类中的列表中可用,以便可以立即执行。请注意,属性可以互换,因此无法通过使用继承自原始类的其他类来解决这一问题。在一个理想的世界里,它看起来是这样的:Python 使用基于装饰器的方法注册表初始化,python,class,decorator,Python,Class,Decorator,我有一个类,它有几个方法,每个方法都有特定的属性(从质量上讲)。我希望这些方法在类中的列表中可用,以便可以立即执行。请注意,属性可以互换,因此无法通过使用继承自原始类的其他类来解决这一问题。在一个理想的世界里,它看起来是这样的: class MyClass: def __init__(): red_rules = set() blue_rules = set() hard_rules = set() soft_rules =
class MyClass:
def __init__():
red_rules = set()
blue_rules = set()
hard_rules = set()
soft_rules = set()
@red
def rule_one(self):
return 1
@blue
@hard
def rule_two(self):
return 2
@hard
def rule_three(self):
return 3
@blue
@soft
def rule_four(self):
return 4
当类被实例化时,通过组合集合和执行方法,应该很容易执行所有红色和软规则。不过,这方面的装饰器很棘手,因为常规注册装饰器可以填充全局对象,但不能填充class属性:
def red(fn):
red_rules.add(fn)
return fn
如何实现这样的功能?您可以将
集合子类化,并为其提供一个decorator方法:
class MySet(set):
def register(self, method):
self.add(method)
return method
class MyClass:
red_rules = MySet()
blue_rules = MySet()
hard_rules = MySet()
soft_rules = MySet()
@red_rules.register
def rule_one(self):
return 1
@blue_rules.register
@hard_rules.register
def rule_two(self):
return 2
@hard_rules.register
def rule_three(self):
return 3
@blue_rules.register
@soft_rules.register
def rule_four(self):
return 4
或者,如果您发现使用.register
方法很难看,则始终可以定义\uuuu调用\uuuu
方法,将集合本身用作装饰器:
class MySet(set):
def __call__(self, method):
"""Use set as a decorator to add elements to it."""
self.add(method)
return method
class MyClass:
red_rules = MySet()
...
@red_rules
def rule_one(self):
return 1
...
这看起来更好,但不够明确,所以对于其他合作者(或未来的你自己)来说,可能更难理解这里发生了什么
要调用存储的函数,只需在所需的集合上循环,并作为self
参数传入实例:
my_instance = MyClass()
for rule in MyClass.red_rules:
rule(my_instance)
您还可以创建一个实用函数来完成此操作,例如,您可以创建一个MySet.invoke()
方法:
class MySet(set):
...
def invoke(self, obj):
for rule in self:
rule(obj)
现在就打电话:
MyClass.red_rules.invoke(my_instance)
或者你可以让MyClass
来处理这个问题:
class MyClass:
...
def invoke_rules(self, rules):
for rule in rules:
rule(self)
然后在MyClass
的实例上调用此函数:
my_instance.invoke_rules(MyClass.red_rules)
您可以子类化set
,并为其提供一个decorator方法:
class MySet(set):
def register(self, method):
self.add(method)
return method
class MyClass:
red_rules = MySet()
blue_rules = MySet()
hard_rules = MySet()
soft_rules = MySet()
@red_rules.register
def rule_one(self):
return 1
@blue_rules.register
@hard_rules.register
def rule_two(self):
return 2
@hard_rules.register
def rule_three(self):
return 3
@blue_rules.register
@soft_rules.register
def rule_four(self):
return 4
或者,如果您发现使用.register
方法很难看,则始终可以定义\uuuu调用\uuuu
方法,将集合本身用作装饰器:
class MySet(set):
def __call__(self, method):
"""Use set as a decorator to add elements to it."""
self.add(method)
return method
class MyClass:
red_rules = MySet()
...
@red_rules
def rule_one(self):
return 1
...
这看起来更好,但不够明确,所以对于其他合作者(或未来的你自己)来说,可能更难理解这里发生了什么
要调用存储的函数,只需在所需的集合上循环,并作为self
参数传入实例:
my_instance = MyClass()
for rule in MyClass.red_rules:
rule(my_instance)
您还可以创建一个实用函数来完成此操作,例如,您可以创建一个MySet.invoke()
方法:
class MySet(set):
...
def invoke(self, obj):
for rule in self:
rule(obj)
现在就打电话:
MyClass.red_rules.invoke(my_instance)
或者你可以让MyClass
来处理这个问题:
class MyClass:
...
def invoke_rules(self, rules):
for rule in rules:
rule(self)
然后在MyClass
的实例上调用此函数:
my_instance.invoke_rules(MyClass.red_rules)
定义函数时应用修饰符;在一个类中,当该类被定义时。此时此刻还没有实例
您有三种选择:
在类级别注册你的装饰者。这并不像听起来那么干净;您要么必须显式地将其他对象传递给装饰器(red\u rules=set()
,然后@red(red\u rules)
,这样装饰器工厂就可以将函数添加到正确的位置),要么必须使用某种类初始值设定器来选取特别标记的函数;您可以使用定义的基类来实现这一点,此时您可以在名称空间上迭代并找到那些标记(装饰器设置的属性)
让你的\uuuuu init\uuuuuuuuuuu
方法(或\uuuuuuu new\uuuuuuuuuuuu
方法)在类上的所有方法上循环,并查找修饰符放在那里的特殊属性
装饰器只需要为装饰方法添加一个\u rule\u name
或类似的属性,如果getattr(getattr(self,name),“\u rule\u name',None)=rule\u name}
将选择在rule\u name
中定义了正确规则名称的任何方法,那么装饰器将只需要为dir(self)中的名称添加{getattr(self,name)
让你的装饰师生产新的;描述符在创建类对象时被调用。这使您可以访问该类,因此可以向该类添加属性
注意\uuuu init\u subclass\uuuu
和\uuuu set\u name\uuuuu
需要Python 3.6或更新版本;要在早期版本中实现类似的功能,您必须求助于
还要注意的是,当您在类级别注册函数时,您需要显式地将它们与函数绑定。
将它们转换为方法,或者您可以在调用它们时显式地传入self
。您可以通过创建一个专用类来保存规则集,并将该类作为描述符来实现自动化:
import types
from collections.abc import MutableSet
class RulesSet(MutableSet):
def __init__(self, values=(), rules=None, instance=None, owner=None):
self._rules = rules or set() # can be a shared set!
self._instance = instance
self._owner = owner
self |= values
def __repr__(self):
bound = ''
if self._owner is not None:
bound = f', instance={self._instance!r}, owner={self._owner!r}'
rules = ', '.join([repr(v) for v in iter(self)])
return f'{type(self).__name__}({{{rules}}}{bound})'
def __contains__(self, ob):
try:
if ob.__self__ is self._instance or ob.__self__ is self._owner:
# test for the unbound function instead when both are bound, this requires staticmethod and classmethod to be unwrapped!
ob = ob.__func__
return any(ob is getattr(f, '__func__', f) for f in self._rules)
except AttributeError:
# not a method-like object
pass
return ob in self._rules
def __iter__(self):
if self._owner is not None:
return (f.__get__(self._instance, self._owner) for f in self._rules)
return iter(self._rules)
def __len__(self):
return len(self._rules)
def add(self, ob):
while isinstance(ob, Rule):
# remove any rule wrappers
ob = ob._function
assert isinstance(ob, (types.FunctionType, classmethod, staticmethod))
self._rules.add(ob)
def discard(self, ob):
self._rules.discard(ob)
def __get__(self, instance, owner):
# share the set with a new, bound instance.
return type(self)(rules=self._rules, instance=instance, owner=owner)
class Rule:
@classmethod
def make_decorator(cls, rulename):
ruleset_name = f'{rulename}_rules'
def decorator(f):
return cls(f, ruleset_name)
decorator.__name__ = rulename
return decorator
def __init__(self, function, ruleset_name):
self._function = function
self._ruleset_name = ruleset_name
def __get__(self, *args):
# this is mostly here just to make Python call __set_name__
return self._function.__get__(*args)
def __set_name__(self, owner, name):
# register, then replace the name with the original function
# to avoid being a performance bottleneck
ruleset = getattr(owner, self._ruleset_name, None)
if ruleset is None:
ruleset = RulesSet()
setattr(owner, self._ruleset_name, ruleset)
ruleset.add(self)
# transfer controrol to any further rule objects
if isinstance(self._function, Rule):
self._function.__set_name__(owner, name)
else:
setattr(owner, name, self._function)
red = Rule.make_decorator('red')
blue = Rule.make_decorator('blue')
hard = Rule.make_decorator('hard')
soft = Rule.make_decorator('soft')
然后使用:
class MyClass:
@red
def rule_one(self):
return 1
@blue
@hard
def rule_two(self):
return 2
@hard
def rule_three(self):
return 3
@blue
@soft
def rule_four(self):
return 4
您可以使用绑定方法集访问self.red\u规则
等:
>>> inst = MyClass()
>>> inst.red_rules
RulesSet({<bound method MyClass.rule_one of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.blue_rules
RulesSet({<bound method MyClass.rule_two of <__main__.MyClass object at 0x106fe7550>>, <bound method MyClass.rule_four of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.hard_rules
RulesSet({<bound method MyClass.rule_three of <__main__.MyClass object at 0x106fe7550>>, <bound method MyClass.rule_two of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> inst.soft_rules
RulesSet({<bound method MyClass.rule_four of <__main__.MyClass object at 0x106fe7550>>}, instance=<__main__.MyClass object at 0x106fe7550>, owner=<class '__main__.MyClass'>)
>>> for rule in inst.hard_rules:
... rule()
...
2
3
您也可以使用这些规则注册classmethod
和staticmethod
对象:
>>> class Foo:
... @hard
... @classmethod
... def rule_class(cls):
... return f'rule_class of {cls!r}'
...
>>> Foo.hard_rules
RulesSet({<bound method Foo.rule_class of <class '__main__.Foo'>>}, instance=None, owner=<class '__main__.Foo'>)
>>> next(iter(Foo.hard_rules))()
"rule_class of <class '__main__.Foo'>"
>>> Foo.rule_class in Foo.hard_rules
True
>>类Foo:
... @坚硬的
... @类方法
... def规则_类(cls):
... 返回f'rule_类的{cls!r}'
...
>>>Foo.hard_规则
规则集({},实例=None,所有者=)
>>>下一步(国际热核聚变实验堆(Foo.hard_rules))()
“规则(类别)”
>>>Foo.rule\u类在Foo.hard\u规则中
真的
定义函数时应用装饰符;在一个类中,当该类被定义时。此时此刻还没有实例
您有三种选择:
在类级别注册你的装饰者。这并不像听起来那么干净;您要么必须显式地将其他对象传递给装饰器(red\u rules=set()
,然后@red(red\u rules)
,这样装饰器工厂就可以将函数添加到正确的位置),要么必须使用某种类初始值设定器来选取特别标记的函数;您可以使用定义的基类来实现这一点,此时您可以在名称空间上迭代并找到那些标记(装饰器设置的属性)
让你的\uuuuu init\uuuuuuuuuuu
方法(或\uuuuuuu new\uuuuuuuuuuuu
方法)在类上的所有方法上循环,并查找修饰符放在那里的特殊属性
装饰者只需添加\u规则\u名称
或类似属性即可