Как я могу добавить задержку для каждого метода в подклассе Python, когда я не хочу реплицировать каждый метод в родительском классе - PullRequest
3 голосов
/ 23 декабря 2011

Приношу свои извинения, если на этот вопрос уже где-то был дан ответ, но если это так, я не смог найти ответ.

Я хотел бы создать подкласс родительского класса таким образом, чтобы перед каждым вызовом соответствующего метода родительского класса была задержка (например, 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() приведет к задержке в 1 секунду до вызова Parent.method1().

Ответы [ 4 ]

4 голосов
/ 23 декабря 2011

Я думаю, что ранее приведенные ответы на самом деле не учитывали вашу конкретную потребность отложить ВСЕ методы от родительского класса и не обязательно должны их декорировать.Вы сказали, что НЕ хотите копировать метод родительского класса в дочернем классе только для того, чтобы вы могли отложить их.В этом ответе используется та же оболочка задержки из 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 переносит все функции в отложенном декораторе. Если вы хотите убедиться, что некоторые встроенные модули, такие как *Функция 1004 * init не была задержана, вы можете просто проверить это имя в MetaClassFactory и проигнорировать его.

4 голосов
/ 23 декабря 2011

Действительно, здесь у вас есть проект, который включает в себя объект Стратегия .

Ваш лучший подход - исправить родительский класс, включив в него вызов "объекта задержки".Задержка по умолчанию ничего не делает.

Это нарушает ожидаемый набор функций «поэтому мне даже не нужно знать все методы родительского класса».

Поиск метода не имеет удобного __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()
        ...
3 голосов
/ 23 декабря 2011

Решение 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!")
1 голос
/ 23 декабря 2011

Вы можете достичь желаемого, используя метод __getattribute__

class Child(Parent):
    def __init__(self, delay):
        self.delay = delay
    def __getattribute__(self, name):
        attr = object.__getattribute__(self, name)
        if hasattr(attr, '__call__'):
            def proxFct(*args, **kwargs):
                time.sleep(object.__getattribute__(self, "delay"))
                return attr(*args, **kwargs)
            return proxFct
        else:
            return attr

Обновление : обновлено в соответствии с комментарием Делнана

Обновление 2 : Обновлено в соответствии со вторым комментарием Делнана

...