Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用decorator自动注册类方法_Python_Oop_Design Patterns_Decorator_Metaclass - Fatal编程技术网

Python 使用decorator自动注册类方法

Python 使用decorator自动注册类方法,python,oop,design-patterns,decorator,metaclass,Python,Oop,Design Patterns,Decorator,Metaclass,我希望能够创建一个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 co

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

这是可能的吗?

否。装饰程序在函数成为方法之前接收函数,因此您不知道它在哪个类上。

不容易,但如果您使用的是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)
请注意,在元类型类中,方法名称不能等于装饰器名称,因为它们会被元类的
\uuuu new\uuu
方法中的
del
命令自动删除


对于Python2.6,我认为您必须显式地告诉装饰器要使用的类名。

不仅仅是装饰器,不。元类可以在类创建后自动使用。如果您的
寄存器
装饰器只是记录了元类应该做什么,那么您可以执行以下操作:

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')}

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

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')}

没有那么漂亮或优雅,但如果您只在一个类中需要它,可能是最简单的方法:

_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

如果需要类名,请使用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')}

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

  • 使用类装饰器(由@unutbu建议)
  • 使用元类(由@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
    
    1.类装饰器方法
    导入检查
    def class_寄存器(cls):
    对于inspect.getmembers(cls)中的方法名称,\uu:
    method=getattr(cls,方法名称)
    如果hasattr(方法“_prop”):
    cls._propdict.update({f{cls._name.{uu name}.{method_name}):method._prop})
    返回cls
    @类寄存器
    类别MyClass:
    _propdict={}
    @注册(“prop1”、“prop2”)
    定义我的_方法(self、arg1、arg2):
    通过
    @登记册(“第3项”、“第4项”)
    定义我的其他方法(self、arg1、arg2):
    通过
    打印(MyClass.\u propdict)
    
    2.元类方法
    registry={}
    类注册类型(类型):
    定义初始值(cls、名称、基数、属性):
    对于键,attrs.items()中的val:
    属性=getattr(val,“_寄存器”,无)
    如果属性不是“无”:
    注册表[f{name}.{key}]=properties
    类MyClass(元类=RegisteringType):
    @注册(“prop1”、“prop2”)
    定义我的_方法(self、arg1、arg2):
    通过
    @登记册(“第3项”、“第4项”)
    定义我的其他方法(self、arg1、arg2):
    通过
    打印(注册表)
    
    但您可以编写自己的元类(或类装饰器)来查看类的方法,并搜索具有附加
    \u register\u properties
    字段的方法,该字段以前由
    register
    装饰器添加。如果你想要一个例子,请告诉我。如果你有一个子类,我们的类(MyClass),你想做同样的事情呢?如何让元类遍历基类和子类的属性?@jMyles,父类的方法已经在注册表中;您希望它们在子类上再次显示为方法吗?您拥有子类的基元组;在注册子类时,您可以迭代父类/基类的任何或所有类字典,以及新子类的属性(如果这是您的意思的话)。对于使用python3的用户,您需要
    class MyClass(object,metaclass=RegisteringType):
    instead如果在另一个模块中定义了
    MyClass
    ,该操作是否有效?(假设模型导入
    class\u寄存器