我正在构建一个 Python 类,我想在其中将一些实例方法标记为“处理程序”,这些方法将在实例被告知运行时应用。下面是一个最小的工作示例。
_handlers = []
def handler_marker(marked_fun):
_handlers.append(marked_fun)
return marked_fun
class ManyHandler:
def __init__(self):
pass
def run(self, input):
for handling_fun in _handlers:
handling_fun(self, input)
@handler_marker
def method_1(self, input):
print(input)
@handler_marker
def method_2(self, input):
print(input[::-1])
def method_3(self, input):
print("I'm not marked.")
if __name__ == "__main__":
MH = ManyHandler()
MH.run('Test string')
这行得通,但我不喜欢
_handlers
列表和装饰器在类定义之外。特别是处理程序列表在这里作为类变量感觉更自然。但是,如果我们只是将_handlers
移到类中并将装饰器更改为
def handler_marker(marked_fun):
ManyHandler._handlers.append(marked_fun)
return marked_fun
我们面临两个问题之一:
NameError: name 'ManyHandler' is not defined
,因为装饰器还不知道 ManyHandler 是什么。NameError: name 'handler_marker' is not defined
,因为类在不知道这里的装饰器是什么的情况下无法自行设置。所以我想把装饰器也放在班级里面。但是要访问类变量
_handlers
,装饰器需要引用 ManyHandler
类。我们不能将类作为参数传递给装饰器,因为在首次应用装饰器时,我们仍在构建类的过程中。
到目前为止,我发现的唯一合理的解决方案是设置一个新类来处理处理程序:
class HandlerHandler:
_handlers = []
def __init__(self):
pass
@classmethod
def handler_marker(cls, marked_fun):
cls._handlers.append(marked_fun)
return marked_fun
class ManyHandler:
MetaHandler = HandlerHandler
_handlers = MetaHandler._handlers
handler_marker = MetaHandler.handler_marker
def __init__(self):
pass
def run(self, input):
for handling_fun in type(self)._handlers:
handling_fun(self, input)
@handler_marker
def method_1(self, input):
print(input)
@handler_marker
def method_2(self, input):
print(input[::-1])
def method_3(self, input):
print("I'm not marked.")
if __name__ == "__main__":
MH = ManyHandler()
MH.run('Test string')
它有效,但我不相信这是否是 Pythonic(甚至是理智的)。
所以我的问题是:
执行此操作的最 Pythonic 方法是什么?
创建一个单独的类是完全明智的——这就是类的用途,用于封装状态和行为。但是,这是我清理这种方法的方法:
class HandlerRegistry:
def __init__(self):
self._handlers = []
def register(self, fun):
self._handlers.append(fun)
return fun
def __iter__(self):
yield from self._handlers
class ManyHandler:
handlers = HandlerRegistry()
def run(self, input):
for handling_fun in self.handlers:
handling_fun(self, input)
@handlers.register
def method_1(self, input):
print(input)
@handlers.register
def method_2(self, input):
print(input[::-1])
def method_3(self, input):
print("I'm not marked.")
mh = ManyHandler()
mh.run('Test string')