将输入函数传递给多个 Class 装饰器

Pass Input Function to Multiple Class Decorators

我有一个 Timer 装饰器,它在屏幕上打印 Memoize 装饰功能所经过的时间。但是,装饰器 print 语句将 memoize class 作为函数名称打印在屏幕上,而不是 memoize 的函数输入。例如,使用此代码:

from memoization import Memoize


import time


import logging


from timer import Timer


@Timer
@Memoize
def pass_and_square_time(seconds):
    time.sleep(seconds)
    return seconds**2


def main():
    logging.getLogger().setLevel(logging.ERROR)

    print '\nFor pass_and_square_time({30}):'.format(n=num)
    print '\n\tThe initial call of pass_and_square_time(30) yields: {ret}'.format(ret=pass_and_square_time(30))
    print '\n\tThe second call of pass_and_square_time(30) yields: {ret}'.format(ret=pass_and_square_time(30))

returns 以下:

For pass_and_square_time(30):
    Timer Time Elapsed: 30.0 seconds

    <memoization.Memoize object at 0x02E5BBD0> 30.0 seconds

    The initial call of pass_and_square_time(30) yields: 900
    Timer Time Elapsed: 0.0 seconds

    <memoization.Memoize object at 0x02E5BBD0> 0.0 seconds

    The second call of pass_and_square_time(30) yields: 900

当我希望 memoization.Memoize 成为 pass_and_square_time 时。我尝试了 self.__wrapper__functools.wrapsfunctools.update_wrapper() 的各种不同组合,但均无济于事。

我的定时器class实现如下:

class Timer(object):
    def __init__(self, fcn=None, timer_name='Timer'):
        self._start_time = None
        self._last_timer_result = None
        self._display = 'seconds'
        self._fcn = fcn
        self._timer_name = timer_name

    def __call__(self, *args):
        self.start()
        fcn_res = self._fcn(*args)
        self.end()
        print '\n\t{func} {time} seconds'.format(func=self._fcn, time=self.last_timer_result)
        return fcn_res

    def __get__(self, obj, objtype):
        return partial(self.__call__, obj)

    '''
    start(), end(), and last_timer_result functions/properties implemented 
    below in order to set the start_time, set the end_time and calculate the 
    last_timer_result,  and return the last_timer_result. I can include more
    if you need it. I didn't include it just because I didn't want to make
    the post too long
    '''

我的Memoizeclass实现如下:

from functools import update_wrapper, partial


class Memoize(object):
    def __init__(self, fcn):
        self._fcn = fcn
        self._memo = {}
        update_wrapper(self, fcn)

    def __call__(self, *args):
        if args not in self._memo:
            self._memo[args] = self._fcn(*args)

        return self._memo[args]

    def __get__(self, obj, objtype):
        return partial(self.__call__, obj)

使用闭包比 类 简单得多。

import functools
import time

def memoize(f):
    _memo = {}
    @functools.wraps(f)
    def _(*args):
        if args not in _memo:
            _memo[args] = f(*args)
        return _memo[args]
    return _                

我同样会把 timer 写成

def timer(f):
    @functools.wraps(f)
    def _(*args):
        start = time.time()
        rv = f(*args)
        end = time.time()
        print '\n\t{func} {t} seconds'.format(func=f.__name__, t=end - start)
        return rv
    return _

然后

 @timer
 @memoize
 def pass_and_square_time(seconds):
     time.sleep(seconds)
     return seconds**2

TimerMemoize 都实现为 classes,这样它们在装饰时 return 是它们自己的 class 的一个实例一个功能。因此,不可能在 returned 函数上使用 functools.wrap 或设置 __name__(因为没有 returned 函数)。

相反,我建议在 TimerMemoize 上实现 __repr__,这将更好地表示装饰函数。 Python 解释器在需要获取对象的字符串表示时将调用该函数。

这是现状和问题:

def pass_and_square_time(seconds):
    time.sleep(seconds)
    return seconds**2

print(pass_and_square_time)         # <function pass_and_square_time at 0x00000000029A1620>
print(Memoize(pass_and_square_time))  # <Memoize object at 0x000000000295BDA0>

现在,如果您将此添加到 Memoize

class Memoize(object):
    ...
    def __repr__(self):
        return repr(self._fcn)

然后它按预期工作:

print(pass_and_square_time)         # <function pass_and_square_time at 0x00000000029A1620>
print(Memoize(pass_and_square_time))  # <function pass_and_square_time at 0x00000000029A1620>

然后对 Memoize 做同样的事情,即使这样也行得通:

print(Timer(Memoize(pass_and_square_time)))  # <function pass_and_square_time at 0x00000000029A1620>

请注意 Timer(Memoize(pass_and_square_time)) 正是装饰器在这里所​​做的:

@Timer
@Memoize
def pass_and_square_time(seconds):
    time.sleep(seconds)
    return seconds**2