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

问题描述 投票:2回答:1

我想写一个抽象的基类。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 确切地指定了这个功能,但我找不到任何现代的引用。

python overloading
1个回答
3
投票

其实单靠一个函数装饰器是做不到你想要的,如果你不希望这个函数的 用户来装饰 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. 如: 碱液 所述,你可以使 A 继承 abc.ABC 妆点 runabc.abstractmethod 以确保子类实现它。


0
投票

不要覆盖 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!
© www.soinside.com 2019 - 2024. All rights reserved.