在抽象方法上实现"after"装饰器



我正在尝试编写一个抽象基类A,它将具有一个抽象方法run,用户/开发人员将期望重载该方法。我想强制一些"after"行为自动应用于派生类B,这样在B.run()运行后,将调用另一个标准方法(在数据管道中,这可以是例如提交或回滚事务(。有办法做到这一点吗?

我失败的天真尝试是:

def do_the_other_thing(func): 
def wrapper(): 
func() 
print('doing the other thing!')
return wrapper 
class A: 
@do_the_other_thing 
def run(self): 
pass     
class B(A): 
def run(self): 
print('running!') 
B().run() 
>>> 'running!' 
>>> #'doing the other thing!'     # <--- is there a way to get this?

当然,我可以通过创建一个不同的抽象方法(例如_run(来实现一个变通方法,该方法是从非抽象方法A.run调用的,但这就不那么优雅了。

我可以在2007年看到,PEP3124明确规定了这一功能,但我找不到任何现代参考

如果您不希望用户自己装饰run,那么您实际上无法单独使用函数装饰器来完成您想要的操作。可以使用类装饰器__init_subclass__metaclasses


使用类装饰器

class A:
def run(self):
pass
def do_the_other_thing(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('doing the other thing!')
return wrapper

def pipeline_thing(cls):
cls.run = do_the_other_thing(cls.run)
# do some other work
return cls

@pipeline_thing
class B(A):
def run(self):
print("running!")

或使用__init_subclass__

class A:
def run(self):
pass
def __init_subclass__(cls):
super().__init_subclass__()
cls.run = do_the_other_thing(cls.run)
# do some other work
class B(A):
def run(self):
print("running!")

或使用metaclasses

class AMeta(type):
def __init__(cls, name, bases, attrs, **kwargs):
super().__init__(name, bases, attrs)
cls.run = do_the_other_thing(cls.run)
# do some other work
class A(metaclass=AMeta):
def run(self):
pass
class B(A):
def run(self):
print("running!")

这个例子对元类来说太夸张了(你使用的是metaclass.__init__——元类中最不强大的魔术方法,你的行为可以用__init_subclass__来完成(这是__init_subclass__的预期用途(。以这种方式使用元类会阻止用户使用元类,并且会使代码不必要地复杂化。如果你需要管道来发挥更多的魔力,你可以使用它们(比如说,如果你需要访问__new__(。

我会使用__init_subclass__或类装饰器(@pipe或其他什么(,它可能也将BA混合在一起。正如alkasm所提到的,您可以使A继承自abc.ABC,并用abc.abstractmethod装饰run以确保子类实现它。

不要覆盖run;重写run调用的方法

class A:
def run(self):
self.do_run()
print('doing the other thing!')
def do_run(self):
pass

class B(A):
def do_run(self):
print('running!') 

然后

>>> B().run() 
running!
doing the other thing!

最新更新