【问题标题】:Pass Input Function to Multiple Class Decorators将输入函数传递给多个类装饰器
【发布时间】:2019-01-25 09:59:56
【问题描述】:

我有一个 Timer 装饰器,它在屏幕上打印 Memoize 装饰功能的经过时间。但是,装饰器打印语句将 memoize 类打印为屏幕上的函数名称,而不是 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))

返回以下内容:

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() 的各种不同组合,但均无济于事。

我的 Timer 类实现如下:

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
    '''

我的 Memoize 类实现如下:

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)

【问题讨论】:

  • "我希望 memoization.Memoize 成为 pass_and_square_time" 这真的是你想要的吗?看起来很简单,但我看不出它有什么意义。

标签: python python-2.7 decorator python-decorators memoization


【解决方案1】:

使用闭包比使用类要简单得多。

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

【讨论】:

    【解决方案2】:

    TimerMemoize 都是作为类实现的,它们在装饰函数时返回自己类的实例。因此,不能在返回函数上使用functools.wrap 或设置__name__(因为没有返回函数)。

    相反,我建议在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
    

    【讨论】:

    • 非常感谢您的帮助,如果我能标记 2 个正确答案,我会的,但我最终还是更改了 memoize,这样它就不是一门课了,因为没有真正的理由我把它当作一个班级。不过谢谢!
    • @strwars 没关系。我很高兴你很感激这个答案。无论如何,正如您所写的那样,您只能接受一个答案,但您可以根据需要对任意多个答案进行 +1 ;)
    • 当我获得 15 点声望时,我一定会回圈并给两个 +1。不幸的是,我现在只有 10 岁,哈哈@zvone
    • @strwars 啊,我明白了:)
    • 如果你碰巧对多处理有所了解,我遇到了一个完全不同的问题@zvone ... :D stackoverflow.com/questions/51922119/…
    猜你喜欢
    • 2021-11-15
    • 1970-01-01
    • 2021-11-21
    • 2018-08-24
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 2016-02-14
    • 2014-11-17
    相关资源
    最近更新 更多