我一直在网上搜索这个问题的解决方案,但没有找到任何优雅的东西。
让我们说我有一个基类,它有一个注册表,派生类可以使用它作为装饰器来注册他们的方法:
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
答案 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个参数:self
,key
和output
这是方法运行的输出(而不是方法本身)
答案 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'}}
您也可以从外部呼叫所有成员,一切都按预期工作。