如何在不重复父类每个方法的情况下,为Python子类中的每个方法添加延迟?

3 投票
4 回答
1500 浏览
提问于 2025-04-17 08:55

抱歉如果这个问题已经在其他地方被回答过,但我找不到答案。

我想创建一个子类,这个子类是从一个父类派生出来的。我的目标是在每次调用父类的方法之前,先有一个延迟(比如用 time.sleep())。我希望这样做,不需要在子类中重复每一个父类的方法。实际上,我想要一个通用的方法,能够适用于几乎任何父类,这样我甚至不需要知道所有父类的方法。

这个延迟的时间会在创建子类实例的时候指定。

举个例子:

class Parent():
    ....
    def method1(self):
        ....
    def method2(self):
        ....


class Child(Parent):
    def __init__(self, delay)
        self.delay = delay
        ....

child = Child(1)

当调用 child.method1() 时,会在调用 Parent.method1() 之前先延迟1秒。

4 个回答

3

S.Lott的解决方案很好。如果你需要更细致的控制(也就是说,只想延迟某些方法,而不是所有方法),你可以使用装饰器:

from time import sleep

def delayed(func):
    '''This is the decorator'''
    def wrapped(*args, **kwargs):
        sleep(2)
        func(*args, **kwargs)
    return wrapped

class Example(object):

    @delayed
    def method(self, str):
        print str

e = Example()
print "Brace! I'm delaying!"
e.method("I'm done!")

这个想法是,你在想要延迟的方法定义前面加上@delayed


补充:如果你想要更细致的控制:设置一个任意的延迟时间:

from time import sleep

def set_delay(seconds):
    def delayed(func):
        '''This is the decorator'''
        def wrapped(*args, **kwargs):
            sleep(seconds)
            func(*args, **kwargs)
        return wrapped
    return delayed

class Example(object):

    @set_delay(1)
    def method(self, str):
        print str

    @set_delay(2)
    def method_2(self, str):
        print str

e = Example()
print "Brace! I'm delaying!"
e.method("I'm done!")
e.method_2("I'm also done!")
4

我觉得之前的回答没有真正解决你想要的需求,就是要延迟父类的所有方法,而不需要一个个去装饰它们。你提到你不想在子类中复制父类的方法,只是为了能延迟它们。这个答案使用了S.Lott提供的延迟包装器,同时还用到了元类(你可以查看这个链接了解更多:http://www.voidspace.org.uk/python/articles/metaclasses.shtml)

#!/usr/bin/env python

from types import FunctionType
import time


def MetaClassFactory(function):
    class MetaClass(type):
        def __new__(meta, classname, bases, classDict):
            newClassDict = {}
            for attributeName, attribute in classDict.items():
                if type(attribute) == FunctionType:
                    attribute = function(attribute)

                newClassDict[attributeName] = attribute
            return type.__new__(meta, classname, bases, newClassDict)
    return MetaClass


def delayed(func):
    def wrapped(*args, **kwargs):
        time.sleep(2)
        func(*args, **kwargs)
    return wrapped

Delayed = MetaClassFactory(delayed)

class MyClass(object):
    __metaclass__ = Delayed 

    def a(self):
        print 'foo'

    def b(self):
        print 'bar'

MetaClassFactory会把每个函数都包裹在延迟装饰器里。如果你想确保某些内置函数,比如init函数不被延迟,你只需要在MetaClassFactory中检查这个名字,然后忽略它就可以了。

4

其实,你这里的设计涉及到一个策略对象。

最好的办法是修改父类,让它包含一个“延迟对象”的调用。默认的延迟对象是不会做任何事情的。

这样做会违反“我甚至不需要知道所有父类的方法”这个理想的功能。

在方法查找中,没有一个方便的__getmethod__,与__getattribute__相对应;这个缺口让我们很难利用Python内部的机制来调用方法。

class Parent( object ):
    delay= ZeroDelay()
    def method1(self):
        self.delay()
        ....
    def method2(self):
        self.delay()
        ...

class ZeroDelay( object ):
    def __call__( self ):
        pass

class ShortDelay( ZeroDelay ):
    def __init__( self, duration=1.0 )
        self.duration= duration
    def __call__( self ):
        time.sleep( self.duration )

class Child( Parent ):
    delay= ShortDelay( 1 )

编辑:当然,你也可以给每个方法添加装饰器。

def delayed( delayer ):
    def wrap( a_method ):
        def do_delay( *args, **kw ):
            delayer()
            return a_method( *args, **kw )
        return do_delay
    return wrap


class Parent( object ):
    delay= ZeroDelay()
    @delayed( self.delay )
    def method1(self):
        self.delay()
        ....
    @delayed( self.delay )
    def method2(self):
        self.delay()
        ...

撰写回答