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?

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top