使用装饰器自动注册类方法

ada*_*amk 17 python oop design-patterns metaclass decorator

我希望能够创建一个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...
Run Code Online (Sandbox Code Playgroud)

我希望在加载完成时,某个地方会有一个dict包含:

{ "my_class.my_method"       : ( prop1, prop2 )
  "my_class.my_other_method" : ( prop3, prop4 ) }
Run Code Online (Sandbox Code Playgroud)

这可能吗?

Mat*_*son 17

不只是一个装饰,不.但是一个元类在创建后可以自动使用它.如果您的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
Run Code Online (Sandbox Code Playgroud)

印花

{'MyClass.my_other_method': ('prop3', 'prop4'), 'MyClass.my_method': ('prop1', 'prop2')}
Run Code Online (Sandbox Code Playgroud)

  • 对于那些使用 python3 的人,您需要 `class MyClass(object, metaclass= RegisteringType):` (2认同)

unu*_*tbu 15

这是对类装饰者的一点爱.我认为语法比元类所需的语法稍微简单一些.

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')}
Run Code Online (Sandbox Code Playgroud)


Man*_*ert 6

如果您需要类名,请使用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
Run Code Online (Sandbox Code Playgroud)

打印

{'my_other_method': ('prop3', 'prop4'), 'my_method': ('prop1', 'prop2')}
Run Code Online (Sandbox Code Playgroud)


Sea*_*123 6

要总结、更新和解释现有答案,您有两种选择:

  1. 使用类装饰器(由@unutbu建议)
  2. 使用元类(由@Matt Anderson建议)

然而,它们都依赖于为函数提供一个属性,以便可以识别它:

def register(*args):
    """
    Creates an attribute on the method, so it can
    be discovered by the metaclass
    """

    def decorator(f):
        f._register = args
        return f

    return decorator
Run Code Online (Sandbox Code Playgroud)

1. 类装饰器方法

def register(*args):
    """
    Creates an attribute on the method, so it can
    be discovered by the metaclass
    """

    def decorator(f):
        f._register = args
        return f

    return decorator
Run Code Online (Sandbox Code Playgroud)

2. 元类方法

import inspect

def class_register(cls):
    for method_name, _ in inspect.getmembers(cls):
        method = getattr(cls, method_name)
        if hasattr(method, "_prop"):
            cls._propdict.update({f"{cls.__name__}.{method_name}": method._prop})
    return cls


@class_register
class MyClass:

    _propdict = {}

    @register("prop1", "prop2")
    def my_method(self, arg1, arg2):
        pass

    @register("prop3", "prop4")
    def my_other_method(self, arg1, arg2):
        pass


print(MyClass._propdict)

Run Code Online (Sandbox Code Playgroud)


Lie*_*yan 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
Run Code Online (Sandbox Code Playgroud)