代码之家  ›  专栏  ›  技术社区  ›  adamk

使用decorator自动注册类方法

  •  16
  • adamk  · 技术社区  · 16 年前

    我希望能够创建一个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 ) }
    

    这有可能吗?

    6 回复  |  直到 9 年前
        1
  •  17
  •   Matt Anderson    16 年前

    不只是一个修饰器,但元类可以在创建后自动与类一起工作。如果你 register decorator只是记录元类应该做什么,您可以执行以下操作:

    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
  •  14
  •   unutbu    9 年前

    这是对课堂装饰师的一点热爱。我认为语法比元类所需的要简单一些。

    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
  •  5
  •   Manuel Ebert    14 年前

    如果需要类名,请使用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')}
    
        4
  •  3
  •   TheUC Duncan    14 年前

    不容易,但是如果您使用的是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)
    

    注意,在元类型类中,方法名不能等于decorator名称,因为它们会被 del 元类中的命令 __new__ 方法。

    对于python 2.6,我认为您必须明确地告诉修饰符要使用的类名。

        5
  •  2
  •   Lie Ryan Bryan    16 年前

    如果您只需要在一个类中使用它,那么这可能是最简单的方法:

    _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
  •  0
  •   Ignacio Vazquez-Abrams    16 年前

    不知道。修饰符在成为方法之前接收函数,所以您不知道它在哪个类上。