Автоматическая регистрация методов класса с использованием декоратора
Я хочу иметь возможность создать декоратор 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...
Я хочу, чтобы при загрузке, где-то там будет dict, содержащий:
{ "my_class.my_method" : ( prop1, prop2 )
"my_class.my_other_method" : ( prop3, prop4 ) }
Возможно ли это?
Ответы
Ответ 1
Не только с декоратором, нет. Но метакласс может автоматически работать с классом после его создания. Если ваш декоратор 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')}
Ответ 2
Здесь немного любви к декораторам класса. Я думаю, что синтаксис немного проще, чем требуется для метаклассов.
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')}
Ответ 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
print
{'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')}
Ответ 4
Нелегко, но если вы используете 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
в методе metaclass __new__
.
Для Python 2.6 я думаю, что вам нужно будет явно указать декоратору имя класса для использования.
Ответ 5
Не такой красивый или элегантный, но, вероятно, самый простой способ, если вам нужно только это только в одном классе:
_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
Ответ 6
Нет. Декоратор получает функцию до того, как она станет методом, поэтому вы не знаете, на каком классе она находится.