使用装饰器自动注册类方法
Posted
技术标签:
【中文标题】使用装饰器自动注册类方法【英文标题】:Auto-register class methods using decorator 【发布时间】:2011-03-04 12:28:25 【问题描述】:我希望能够创建一个 python 装饰器,它自动在全局存储库中“注册”类方法(带有一些属性)。
示例代码:
class my_class(object):
@register(prop1,prop2)
def my_method( arg1,arg2 ):
# method code here...
@register(prop3,prop4)
def my_other_method( arg1,arg2 ):
# method code here...
我希望在加载完成后,某处会有一个包含:
"my_class.my_method" : ( prop1, prop2 )
"my_class.my_other_method" : ( prop3, prop4 )
这可能吗?
【问题讨论】:
【参考方案1】:这是对类装饰器的一点爱。我认为语法比元类所需的语法稍微简单一些。
def class_register(cls):
cls._propdict =
for methodname in dir(cls):
method = getattr(cls, methodname)
if hasattr(method, '_prop'):
cls._propdict.update(
cls.__name__ + '.' + methodname: method._prop)
return cls
def register(*args):
def wrapper(func):
func._prop = args
return func
return wrapper
@class_register
class MyClass(object):
@register('prop1', 'prop2')
def my_method(self, arg1, arg2):
pass
@register('prop3', 'prop4')
def my_other_method(self, arg1, arg2):
pass
myclass = MyClass()
print(myclass._propdict)
# 'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')
【讨论】:
如果MyClass
在另一个模块中定义,这是否有效? (假设模型导入class_register
)【参考方案2】:
不只是一个装饰器,不。但是元类可以在创建后自动与类一起使用。如果您的 register
装饰器只是记下元类应该做什么,您可以执行以下操作:
registry =
class RegisteringType(type):
def __init__(cls, name, bases, attrs):
for key, val in attrs.iteritems():
properties = getattr(val, 'register', None)
if properties is not None:
registry['%s.%s' % (name, key)] = properties
def register(*args):
def decorator(f):
f.register = tuple(args)
return f
return decorator
class MyClass(object):
__metaclass__ = RegisteringType
@register('prop1','prop2')
def my_method( arg1,arg2 ):
pass
@register('prop3','prop4')
def my_other_method( arg1,arg2 ):
pass
print registry
打印
'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')
【讨论】:
如果你有一个子类 OurClass(MyClass) - 你想做同样的事情怎么办?如何让 metaclass 遍历 base 和 child 的属性? @jMyles,父类的方法已经在注册表中;您希望它们再次显示为子类的方法吗?您有子类的基本元组;在进行子类的注册时(如果这就是您的意思),您可以遍历父/基类的任何或所有类字典,以及新子类的属性。 使用python3的,需要class MyClass(object, metaclass= RegisteringType):
代替【参考方案3】:
如果您需要类名,请使用 Matt 的解决方案。但是,如果您可以在注册表中只使用方法名称(或对该方法的引用),这可能是一种更简单的方法:
class Registry:
r =
@classmethod
def register(cls, *args):
def decorator(fn):
cls.r[fn.__name__] = args
return fn
return decorator
class MyClass(object):
@Registry.register("prop1","prop2")
def my_method( arg1,arg2 ):
pass
@Registry.register("prop3","prop4")
def my_other_method( arg1,arg2 ):
pass
print Registry.r
打印
'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')
【讨论】:
【参考方案4】:没有那么漂亮或优雅,但如果您只需要在一个类中使用它可能是最简单的方法:
_registry =
class MyClass(object):
def register(*prop):
def decorator(meth):
_registry[MyClass.__name__ + '.' + meth.__name__] = prop
return decorator
@register('prop1', 'prop2')
def my_method(self, arg1, arg2):
pass
@register('prop3', 'prop4')
def my_other_method(self, arg1, arg2):
pass
del register
【讨论】:
【参考方案5】:要总结、更新和解释现有答案,您有两种选择:
-
使用类装饰器(@unutbu 建议)
使用元类(@Matt Anderson 建议)
但是,它们都依赖于给函数一个属性,以便它可以被识别:
def register(*args):
"""
Creates an attribute on the method, so it can
be discovered by the metaclass
"""
def decorator(f):
f._register = args
return f
return decorator
1。类装饰器方法
import inspect
def class_register(cls):
for method_name, _ in inspect.getmembers(cls):
method = getattr(cls, method_name)
if hasattr(method, "_prop"):
cls._propdict.update(f"cls.__name__.method_name": method._prop)
return cls
@class_register
class MyClass:
_propdict =
@register("prop1", "prop2")
def my_method(self, arg1, arg2):
pass
@register("prop3", "prop4")
def my_other_method(self, arg1, arg2):
pass
print(MyClass._propdict)
2。元类方法
registry =
class RegisteringType(type):
def __init__(cls, name, bases, attrs):
for key, val in attrs.items():
properties = getattr(val, "_register", None)
if properties is not None:
registry[f"name.key"] = properties
class MyClass(metaclass=RegisteringType):
@register("prop1", "prop2")
def my_method(self, arg1, arg2):
pass
@register("prop3", "prop4")
def my_other_method(self, arg1, arg2):
pass
print(registry)
【讨论】:
感谢您的总结!方法 2 对我有用,但方法 1 没有注册任何方法(使用上面代码的精确副本。我使用的是 Python 3.9。【参考方案6】:不容易,但如果您使用的是 Python 3,这应该可以:
registry =
class MetaRegistry(type):
@classmethod
def __prepare__(mcl, name, bases):
def register(*props):
def deco(f):
registry[name + "." + f.__name__] = props
return f
return deco
d = dict()
d['register'] = register
return d
def __new__(mcl, name, bases, dct):
del dct['register']
cls = super().__new__(mcl, name, bases, dct)
return cls
class my_class(object, metaclass=MetaRegistry):
@register('prop1','prop2')
def my_method( arg1,arg2 ):
pass # method code here...
@register('prop3','prop4')
def my_other_method( arg1,arg2 ):
pass # method code here...
print(registry)
请注意,元类型类中的方法名称不能与装饰器名称相同,因为它们会被元类的__new__
方法中的del
命令自动删除。
对于 Python 2.6,我认为您必须明确告诉装饰器要使用的类名。
【讨论】:
【参考方案7】:没有。装饰器在函数变成方法之前接收函数,所以你不知道它在哪个类上。
【讨论】:
但是您可以编写自己的元类(或类装饰器)来查看该类的方法并搜索具有附加_register_properties
字段的方法,该字段由之前的 register
装饰器添加.请告诉我,如果你想要一个例子。以上是关于使用装饰器自动注册类方法的主要内容,如果未能解决你的问题,请参考以下文章