python - 在抽象方法上实现“后”装饰器
问题描述
我正在尝试编写一个抽象基类A
,它将具有一个抽象方法run
,用户/开发人员将被期望重载。我想强制一些“之后”行为自动应用于派生类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 年PEP 3124就可以看到这种方式准确地指定了这个功能,但我找不到任何现代参考。
解决方案
如果您不希望用户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
可能B
与A
. 正如alkasm 所提到的,您可以进行A
继承abc.ABC
和装饰run
以abc.abstractmethod
确保子类实现它。
推荐阅读
- css - IE11 flex:1 在父级没有固定高度时导致内容溢出
- hibernate - GenerationType.SEQUENCE 生成负主键
- python - 获取以前的控制台输出作为脚本中的字符串
- excel - 如何自动排序表
- python - 如何提高python中的计算速度?
- flutter - Flutter 解码错误——“flutter: NoSuchMethodError: Class '_InternalLinkedHashMap
' 没有实例方法 'decode'。” - amazon-web-services - 在cloudformation中,你可以从另一个区域导入api key吗?
- kubernetes - 节点资源不足时的 Kubernetes 节点扩展
- html - 所有元素都在导航栏下方
- apache-spark - 我不明白为什么最后阶段没有任何保存或追加数据操作