Python 实施";“之后”;一种抽象方法上的装饰器
我正在尝试编写一个抽象基类Python 实施";“之后”;一种抽象方法上的装饰器,python,overloading,Python,Overloading,我正在尝试编写一个抽象基类A,它将有一个抽象方法运行,用户/开发人员可能会过载。我希望强制将一些“after”行为自动应用于派生类B,以便在B.run()运行它的过程后,将调用另一个标准方法(在数据管道中,这可能是提交或回滚事务)。有没有办法做到这一点 我在这方面失败的天真尝试是: def做其他事情(func): def包装器(): func() 打印('做其他事情!') 返回包装器 A类: @做另一件事 def运行(自): 通过 B(A)类: def运行(自): 打印('正在运行!') B()
A
,它将有一个抽象方法运行
,用户/开发人员可能会过载。我希望强制将一些“after”行为自动应用于派生类B
,以便在B.run()
运行它的过程后,将调用另一个标准方法(在数据管道中,这可能是提交或回滚事务)。有没有办法做到这一点
我在这方面失败的天真尝试是:
def做其他事情(func):
def包装器():
func()
打印('做其他事情!')
返回包装器
A类:
@做另一件事
def运行(自):
通过
B(A)类:
def运行(自):
打印('正在运行!')
B().run()
>>>“跑!”
>>>#“做另一件事!”# 如果您不希望用户自己装饰运行,那么您实际上无法单独使用函数装饰器来完成所需的工作。您可以使用类装饰器,或者
有一个班级装饰师
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!")
或者使用\uuuu init\u子类
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!")
或者使用元类
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!")
这个例子对于元类来说是杀伤力太大了(您使用的是元类.\uuuuu init\uuuu
——元类中最不强大的魔法方法,您的行为可以通过\uuuuuu init\u子类\uuuuuu
来完成(这是)。以这种方式使用元类将阻止您的用户使用元类,这将不必要地使您的代码复杂化。如果您需要管道来发挥更大的作用,您可以使用它们(例如,如果您需要访问\uuuuu new\uuu
)
我会使用\uuuu init\u子类\uuuu
或类装饰器(@pipe
或其他东西)正如前面提到的,您可以让A
继承abc.abc
并用abc.abstractmethod
修饰run
,以确保子类实现它。不要重写run
;重写run
调用的方法
然后
Decorators将在定义函数时替换函数,因此我认为没有任何干净的方法可以在不做“标记”的情况下知道函数已被修饰(以便可以将其应用于子类)以某种方式;例如,向函数或类添加属性以供以后阅读。您可以使用\uu init\u subclass\uu
循环遍历在子类上定义的所有方法,然后将装饰器应用于带有标记的方法。我想您还必须保存要使用的装饰器,也许标记可以是装饰器本身。元类更简单的是,您可以让元类通过类定义的方法运行,并包装满足某些条件的方法(例如,命名为run
)每个子类也会出现这种情况。请参阅,例如,和使用元类也允许您使用abc。abstractmethod
强制用户在实例化类时重写正确的方法(否则仅在调用方法时出错),请看。我同意-但我认为OP希望避免这种情况,正如他们在问题中所说。这是一种简单的方法(这总是好的).我同意,我认为\uuuuu init\uuuuu subclass\uuuuuu
是解决这一问题的方法,因为它最有意义,也是处理抽象类初始化/父类更改的任何子类的最干净的方法
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!