如何向类添加前/后方法。Python

ori*_*ral 2 python oop polymorphism

假设我有一个 A 类,它有很多方法,但我希望它在调用每个方法之前和之后运行某些行。例如:我希望我的类 Dog 在每次调用 bark() 或 run() 时运行 before() 和 after() 。

class Dog():
    def __init__(self, sound, speed):
        self.sound = sound
        self.speed = speed

    def before(self):
        check_some_things(self)

    def after(self):
        do_some_things(self)

    def bark(self):
        sound(self.sound)

    def run(self):
        move(self.speed)
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 7

您可以将其封装在装饰器中;如果这些装饰器在 上可用before,则以下装饰器将调用:afterself

import inspect
from functools import wraps

def before_and_after(f):
    @wraps(f)
    def wrapper(self, *args, **kw):
        if hasattr(self, 'before') and inspect.ismethod(self.before):
            self.before()
        result = f(self, *args, **kw)
        if hasattr(self, 'after') and inspect.ismethod(self.after):
            self.after()
        return result
    return wrapper
Run Code Online (Sandbox Code Playgroud)

然后简单地应用于应该包装的方法:

class Dog():
    def __init__(self, sound, speed):
        self.sound = sound
        self.speed = speed

    def before(self):
        check_some_things(self)

    def after(self):
        do_some_things(self)

    @before_and_after
    def bark(self):
        sound(self.sound)

    @before_and_after
    def run(self):
        move(self.speed)
Run Code Online (Sandbox Code Playgroud)

装饰器假设它用于方法,例如生成的包装器期望self作为第一个参数。

如果这需要应用于所有不是before或 的方法after,也许元类是合适的:

class BeforeAfterMeta(type):
    def __new__(mcs, classname, bases, body):
        for name, value in body.items():
            if not inspect.isfunction(value):
                continue
            if name in ('before', 'after') or name[:2] + name[-2:] == '_' * 4:
                # before or after hook, or a special method name like __init__.
                continue
            body[name] = before_and_after(value)
        return super(BeforeAfterMeta, mcs).__new__(mcs, classname, bases, body)
Run Code Online (Sandbox Code Playgroud)

然后您可以将其应用到您的班级:

class Dog(metaclass=BeforeAfterMeta):
    def __init__(self, sound, speed):
        self.sound = sound
        self.speed = speed

    def before(self):
        check_some_things(self)

    def after(self):
        do_some_things(self)

    def bark(self):
        sound(self.sound)

    def run(self):
        move(self.speed)
Run Code Online (Sandbox Code Playgroud)