python decorator - 在base中注册类

时间:2017-05-11 12:32:40

标签: python

我一直在网上搜索这个问题的解决方案,但没有找到任何优雅的东西。

让我们说我有一个基类,它有一个注册表,派生类可以使用它作为装饰器来注册他们的方法:

from abc import ABCMeta


class Base(object):
    __metaclass__ = ABCMeta

    def __init__(self, name):
        self._name = name
        self._content = {}

    def run_them_all(self):
        for key, content in self._content.items():
            print(key, content)

    # This should be the registery function
    def register(old_method, key):
        self._content[key] = old_method()


class Derived(Base):
    @Base.register("some other content")
    def do_something(self):
        return {"name": "yes"}

    @Base.register("some_content")
    def do_something_else(self):
        return {"hi": "ho"}

    def this_should_not_be_registered(self):
        return "yooo"


derived = Derived("John")
derived.run_them_all()

有可能实现这个目标吗? 拥有一个普通的装饰器需要一个显式的函数调用来创建绕道。但是我只想注册这些调用以供以后使用,或者至少注册它们的返回值以供以后使用而不直接自己调用这些方法。

这应该导致:

{"some other content": {"name": "yes"}}
{"some content": {"hi": "ho"}}

我只是想避免像这样覆盖run_them_all:

class Derived(Base):
    ...

    def run_them_all(self):
        self._content["some_content"] = self.do_something_else()
        ...
        return self._content

2 个答案:

答案 0 :(得分:1)

为什么不简单地将装饰器定义为外部函数(它可以在类中完成,但感觉很麻烦):

def register(key):
    def register_decorator(function):
        def fn(self, *args, **kw):
            out = function(self, *args, **kw)
            self.register(key, out)
        return fn
    return register_decorator

使用它是这样的:

class BlaBla(Base):
    ...

    @register
    def do_something(self):
        return {"name": "yes"}

此外,您的Base.register函数应该收到3个参数:selfkeyoutput这是方法运行的输出(而不是方法本身)

答案 1 :(得分:0)

我找到了解决这个问题的方法,但这并不优雅。

我创建了一个元类,它将在创建派生类时添加实例变量:

import types


class MetaTemplateData(type):
    def __new__(mcs, name, base, dct):
        orig_init = dct.get("__init__")
        decorators = []
        for _, value in dct.items():
            if isinstance(value, types.FunctionType):
                if value.__name__ == "_content_wrapper":
                    decorators.append(value)
            elif isinstance(value, staticmethod):
                function = value.__func__
                if function.__name__ == "_content_wrapper":
                    decorators.append(function)

        def init_wrapper(self, *args, **kwargs):
            if orig_init:
                orig_init(self, *args, **kwargs)

            # pylint: disable=protected-access
            self._callbacks = getattr(self, "_callbacks", [])
            self._callbacks.extend(decorators)
        dct["__init__"] = init_wrapper
        return type.__new__(mcs, name, base, dct)

这是我们的装饰器,这将调用该函数并检查是否为静态并将其结果存储在新的字典中:

def add_content(key):
    def register_decorator(function):
        def _content_wrapper(self=None, *args, **kwargs):
            num_args = function.__code__.co_argcount
            if isinstance(function, types.FunctionType):
                if self and num_args != 0:
                    data = function(self, *args, **kwargs)
                else:
                    data = function(*args, **kwargs)
            else:
                data = function.__func__()
            return {key: data}
        return _content_wrapper
    return register_decorator

我们可以创建一个这样的基类,它负责为我们调用所有装饰成员并将其结果存储在dict中:

class Base(object, metaclass=MetaTemplateData):
    def __init__(self, email):
        self._email = email
        self._callbacks = []

    def parse(self):
        content = {}
        for func in self._callbacks:
            content.update(func(self))
        return content

最后我们得出派生类,它将负责添加内容:

class Derived(Base):
    def __init__(self, name, email):
        super().__init__(email)
        self._name = name

    @add_content("key_number_one")
    def something(self):
        return {
            "email": self._email
        }

    @add_content("key_two")
    def something_else(self):
        return {
            "email": "DAEMAIL" + self._email
        }

    @add_content("key_for_static")
    @staticmethod
    def _do_private_things():
        return {
            "oh lord": "hoho"
        }

现在,当我们创建派生类的实例时,神奇的事情发生了:

derived = Derived("John", "some@email.com")
content = derived.parse()
print(content)

结果:

  

{'key_for_static':{'oh lord':'hoho'},'key_number_one':{'email':'some@email.com'},'key_two':{'email':'DAEMAILsome @ email .COM'}}

您也可以从外部呼叫所有成员,一切都按预期工作。