Auto-Register Klassenmethoden Dekorateur mit
-
27-09-2019 - |
Frage
Ich möchte ein Python-Dekorateur in der Lage sein zu schaffen, die automatisch „Register“ Klassenmethoden in einem globalen Repository (mit einigen Eigenschaften).
Beispielcode:
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...
Ich möchte, dass beim Laden fertig ist, irgendwo wird es eine dict sein enthalten:
{ "my_class.my_method" : ( prop1, prop2 )
"my_class.my_other_method" : ( prop3, prop4 ) }
Ist das möglich?
Lösung
Nicht nur mit einem Dekorateur, nein. Aber ein Metaklasse kann automatisch mit einer Klasse arbeitet nach ihrer geschaffen. Wenn Ihr register
Dekorateur macht nur Notizen über das, was die Metaklasse tun sollten, können Sie die folgenden Schritte ausführen:
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')}
Andere Tipps
Hier ist eine wenig Liebe für die Klasse Dekorateure. Ich denke, die Syntax ist etwas einfacher als die für metaclasses erforderlich.
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')}
Wenn Sie den Klassen Namen benötigen, verwenden Matts Lösung. Wenn Sie jedoch ist ok mit nur die Methode Namen haben - oder einen Verweis auf das Verfahren - in der Registrierung, könnte dies einen einfacheren Weg, es zu tun sein:
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')}
Nicht einfach, aber wenn Sie mit Python 3 sind dies sollte funktionieren:
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)
Beachten Sie, dass Sie nicht Methodennamen gleich zu dem Dekoratornamens in der Meta-typisierte Klasse haben können, weil sie automatisch von dem del
Befehl in der Metaklasse der __new__
Methode gelöscht werden.
Für Python 2.6 Ich denke, Sie explizit den Dekorateur die Klassennamen zu verwenden sagen müßten.
Nicht so schön oder elegant, aber wahrscheinlich der einfachste Weg, wenn Sie brauchen dies in einer Klasse nur:
_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
Nein. Der Dekorateur erhält die Funktion, bevor es eine Methode worden ist, so dass Sie nicht wissen, was Klasse ist es auf.