我希望能够创建一个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 ) }
这可能吗?
答案 0 :(得分:17)
不只是一个装饰者,没有。但是一个元类在创建后可以自动使用它。如果您的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')}
答案 1 :(得分:14)
这是对类装饰者的一点爱。我认为语法比元类所需的语法稍微简单。
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')}
答案 2 :(得分:6)
如果您需要类名,请使用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')}
答案 3 :(得分:3)
不容易,但如果您使用的是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)
请注意,您不能在元类型类中使用等于装饰器名称的方法名称,因为它们会被元类的del
方法中的__new__
命令自动删除。
对于Python 2.6,我认为您必须明确告诉装饰器要使用的类名。
答案 4 :(得分:2)
不是那么美丽或优雅,但如果你只需要在一个班级中,这可能是最简单的方法:
_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 :(得分:2)
要总结,更新和解释现有答案,您有两个选择:
但是,它们都依赖于为函数赋予属性,以便可以对其进行识别:
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
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)
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)
答案 6 :(得分:0)
没有。装饰器在成为方法之前接收函数,因此您不知道它在哪个类上。