Python:修饰一个在继承时要覆盖的类方法

Jus*_*tin 7 python inheritance decorator

假设我有一些基类:

class Task:
    def run(self):
        #override this!
Run Code Online (Sandbox Code Playgroud)

现在,我希望其他人继承Task并覆盖run()方法:

class MyTask(Task):
    def run(self):
        #successful override!
Run Code Online (Sandbox Code Playgroud)

但是,问题是必须在每个继承Task的类的run()方法之前和之后发生逻辑.

似乎我可以这样做的一种方法是在基类中定义另一个方法,然后调用run()方法.但是,我想问一下,有没有办法用装饰器来实现这个目的?这样做的最pythonic方式是什么?

unu*_*tbu 10

正如评论中所建议的那样,让子类覆盖一个钩子而不是run它自己可能是最好的:

class Task(object):
    def run(self):
        # before 
        self.do_run()
        # after

class MyTask(Task):
    def do_run(self):
        ...

task = MyTask()
task.run()
Run Code Online (Sandbox Code Playgroud)

然而,这是一种方式,你可以用一个类装饰做到这一点:

def decorate_run(cls):
    run = getattr(cls, 'run')
    def new_run(self):
        print('before')
        run(self)
        print('after')
    setattr(cls, 'run', new_run)
    return cls


class Task(object): pass

@decorate_run
class MyTask(Task):
    def run(self):
        pass

task = MyTask()
task.run()

# prints:
# before
# after
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用元类.使用元类的优点是不必对子类进行修饰.Task可以成为元类的实例,然后所有子类Task将自动继承元类.

class MetaTask(type):
    def __init__(cls, name, bases, clsdict):
        if 'run' in clsdict:
            def new_run(self):
                print('before')
                clsdict['run'](self)
                print('after')
            setattr(cls, 'run', new_run)

class Task(object, metaclass=MetaTask):
    # For Python2: remove metaclass=MetaTask above and uncomment below:
    # __metaclass__ = MetaTask
    pass

class MyTask(Task):
    def run(self):
        #successful override!
        pass

task = MyTask()
task.run()
Run Code Online (Sandbox Code Playgroud)

  • @Jørgen:声明元类的语法在 Python2 和 Python3 之间发生了变化。如果您使用的是 Python3,请将 `metaclass=MetaTask` 添加到类基声明中。我已经编辑了上面的帖子以显示我的意思。 (2认同)