如何在课程中添加前/后方法。蟒蛇

时间:2015-03-23 16:47:36

标签: 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)

3 个答案:

答案 0 :(得分:4)

您可以将其封装在装饰器中;以下装饰者会调用beforeafter,如果self上有这些内容:

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

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

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)

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

如果这需要适用于非beforeafter所有方法,则可能会按顺序使用元类:

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)

然后你可以申请你的班级:

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)

答案 1 :(得分:2)

如果pre和post方法存在,您还可以使用装饰器功能检查class Dog并覆盖run方法:

def PrePostMethod(inputClass):
    mainRun    = inputClass.run
    beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
    afterFunc  = inputClass.after if "after" in inputClass.__dict__ else None
    def new_run(self, *args, **kwargs):
        # you could inspect the given arguments if you need
        # to parse arguments into before and the after methods
        if beforeFunc:
            self.before()
        mainRun(self)
        if afterFunc:
            self.after()
    inputClass.run = new_run
    return inputClass

@PrePostMethod
class Dog(object):

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

    def before(self):
        print "Do stuff before"

    def after(self):
        print "Do stuff after"

    def run(self):
        print "Do main process"


Dog(1,2).run()

要将run中的参数和关键字参数解析为beforeafter,请使用类inspect并循环遍历args和kwargs来解析正确的参数。

from inspect import getargspec
def argHandler(method, *args, **kwargs):
    method  = getargspec(method)
    mArgs   = method.args
    mKwargs = method.keywords
    rArgs    =  args[:len(mArgs)-1]
    rKwargs  = { k:v for k,v in kwargs.iteritems() if k in mKwargs }
    leftArgs = len(mArgs)-len(rArgs)
    if len(rKwargs):
        rKwargs = [ rKwargs[k] for k in mArgs[:leftArgs-1]]
        rArgs  += rKwargs
    return rArgs

def PrePostMethod(inputClass):
    mainRun    = inputClass.run
    beforeFunc = inputClass.before if "before" in inputClass.__dict__ else None
    afterFunc  = inputClass.after if "after" in inputClass.__dict__ else None
    def new_run(self, *args, **kwargs):
        if beforeFunc:
            nargs = argHandler(self.before, *args, **kwargs)
            if nargs: self.before( *nargs)
            else: self.before()
        nargs = argHandler(mainRun, *args, **kwargs)
        if nargs: mainRun(self, *nargs)
        else: mainRun(self)
        if afterFunc:
            nargs = argHandler(self.after, *args, **kwargs)
            if nargs:  self.after( *nargs)
            else: self.after()
    inputClass.run = new_run
    return inputClass

答案 2 :(得分:0)

您可以使用许多不同的方法来执行此操作。但我认为最好的方法是,使用前后方法定义一个类,并重新定义它的对象隐藏方法:__enter____exit__。要使用它们,只需使用复合语句with调用该类。

 class pre_post(object):

    def __enter__(self):
        print "Enter check method.."

    def __exit__(self, type, value, tb):
        print "Exit check method.."

class dog(object):

    def run(self, checkups=True):
        if checkups:
            with pre_post() as pp:
                print "My stuff.."
        else:
            print "My stuff.."

dog().run(True)

这会给你以下结果:

Enter check method..
My stuff..
Exit check method..

我希望这会对你有帮助。