métodos de la clase de auto-registro en el decorador
-
27-09-2019 - |
Pregunta
Quiero ser capaz de crear un decorador de Python que automáticamente "registros" métodos de clase en un repositorio mundial (con algunas propiedades).
Ejemplo código:
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...
Quiero que cuando se realiza la carga, en algún lugar habrá un diccionario que contiene:
{ "my_class.my_method" : ( prop1, prop2 )
"my_class.my_other_method" : ( prop3, prop4 ) }
¿Es esto posible?
Solución
No con sólo un decorador, no. Pero una metaclase puede trabajar de forma automática con una clase después de su sido creado. Si su decorador register
sólo hace notas sobre lo que debe hacer la metaclase, puede hacer lo siguiente:
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
impresión
{'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')}
Otros consejos
Aquí hay un poco de amor para decoradores de clase. Creo que la sintaxis es un poco más sencillo que el requerido para metaclases.
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')}
Si necesita el nombre de clases, utilizar la solución de Matt. Sin embargo, si estás bien con sólo tener el nombre de métodos - o una referencia al método - en el registro, esto podría ser una manera más sencilla de hacerlo:
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
La impresión
{'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')}
No es fácil, pero si usted está utilizando Python 3 esto debería funcionar:
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)
Tenga en cuenta que no se puede tener nombres de método igual al nombre de decorador en la clase de meta-escrito, ya que se eliminan automáticamente por el comando del
en el método __new__
de la metaclase.
En Python 2.6 Creo que tendría que decirle explícitamente el decorador el nombre de clase de uso.
No es tan bonito o elegante, pero probablemente la forma más sencilla si sólo necesita esto en una sola clase:
_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
No. El decorador recibe la función antes de que se ha convertido en un método, por lo que no sabe qué clase que se encuentra.