从抽象方法继承包装器

Sil*_*uck 1 python abstract-class abc wrapper python-3.x

我想系统地包装一些基类的重写方法。

我ABC用于基类。我试图包装@abstractmethod,将注释放在之前或之后,但它不起作用。据我了解,整个包装方法被覆盖。

from functools import wraps
from abc import ABC, abstractmethod

def print_before(func):
    @wraps(func)
    def out(*args, **kwargs):
        print('Hello')
        return func(*args, **kwargs)

    return out

class Base(ABC):
    @print_before
    @abstractmethod
    def test(self):
        pass

class Extend(Base):
    def test(self):
        print('World')
Run Code Online (Sandbox Code Playgroud)

这是我们测试时发生的情况:

Extend().test()
Run Code Online (Sandbox Code Playgroud)

结果:

World
Run Code Online (Sandbox Code Playgroud)

期望:

Hello
World
Run Code Online (Sandbox Code Playgroud)

我想我没有使用正确的方法来获得这种行为。在重写方法之前和之后运行一些代码的好的 Pythonic 方式是什么?

Wom*_*atz 5

正如您所注意到的,装饰器不会更改被覆盖的方法。每次创建子类时都可以装饰该方法。您甚至可以使用魔术方法自动执行此操作__init_subclass__。

class Base(ABC):
    ...

    def __init_subclass__(cls):
        cls.test = print_before(cls.test)
Run Code Online (Sandbox Code Playgroud)

但我不会推荐这种方法。如果它们不覆盖该方法,它可能会破坏ABC继承自的机制和类Extend被装饰两次。

这是一个更简单的方法。我们定义了一个具体的“公共”方法,Base它调用了一个抽象的“私有”方法。在子类中,我们必须实现“私有”方法。

class Base(ABC):
    def test(self):
        # do something before
        result = self._do_work()
        # do something after
        return result

    @abstractmethod
    def _do_work(self):
        pass


class Extend(Base):
    def _do_work(self):
        # your implementation here

# use it like this:
e = Extend()
e.test()
Run Code Online (Sandbox Code Playgroud)

另一个优点是您可以更改子类中“包装器”的行为,这对于装饰器来说是困难的。