带有接收参数的函数的 Python 装饰器

Python decorator with a function that receives a parameter

提问人:Gustavo Nunes 提问时间:9/25/2023 最后编辑:Gustavo Nunes 更新时间:9/26/2023 访问量:47

问:

Decorator 未按预期工作

我试图更多地了解装饰器,所以我搜索并得到了许多视频,这些视频使这个确切的功能:

def tictoc(func):
    def wrapper():
        t1 = time.time()
        return func()
        t2 = time.time()-t1
        print(f'Took {t2} seconds to run.')
    return wrapper

所以我做了一个阶乘函数,它接受一个参数(你想看到的阶乘数字)

@tictoc
def fat(num):
    f = 1
    for x in range(1, num+1):
        f *= x
    return f

print(fat(5))

然后我运行了它

但是我收到此错误:TypeError:tictoc..wrapper() 接受 0 个位置参数,但给出了 1 个

有人可以解释一下我做错了什么吗?

python 函数 参数 装饰器

评论

2赞 Goku - stands with Palestine 9/25/2023
欢迎。。请不要使用图片.........复制粘贴代码。
0赞 nokla 9/25/2023
你可以在这里看到原因。
0赞 JonSG 9/25/2023
之后的代码永远不会运行。我很惊讶任何来源都会给你这个包装器的例子。return func()
0赞 JonSG 9/25/2023
这回答了你的问题吗?Python 包装器函数在装饰器中接受参数

答:

0赞 Domenico Spidy Tamburro 9/25/2023 #1

正如 JonSG 所指出的,return 语句会跳过它之后的其余代码,因此永远不会计算 t2,也不会执行打印。

您可能希望重写代码,如下所示:

import time

def tictoc(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        _func = func(*args, **kwargs)
        t2 = time.time()-t1
        print(f'Took {t2} seconds to run.')
        return _func
    return wrapper

@tictoc
def fac(num):
    f = 1
    for x in range(1, num+1):
        f *= x
    return f

print(fac(3))

首先,您需要将参数传递给装饰器。您可以通过在内部包装函数中使用 和 来做到这一点(也许这里是多余的)。然后,内部包装器将这些传递给包装函数。其次,将函数的结果存储到函数中,并在计算 t2 后返回。*args**kwargs**kwargs_func

0赞 Prayson W. Daniel 9/25/2023 #2

问题#1:装饰器不允许传递参数,因为您在没有参数的情况下调用函数

def tictoc(func):
    def wrapper():
        t1 = time.time()
        return func() #<—- here is the 🐛
        t2 = time.time()-t1
        print(f'Took {t2} seconds to run.')
    return wrapper

我们可以纠正:

def tictoc(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        return func(*args, **kwargs) 
        t2 = time.time()-t1 # <— this will not be evaluated as we have returned 
        print(f'Took {t2} seconds to run.') # <- this too
    return wrapper

问题#2:Return语句将返回。T2 和上面评论中指出的打印将不被评估。我们可以解决这个问题:

def tictoc(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        results = func(*args, **kwargs) 
        t2 = time.time()-t1 
        print(f'Took {t2} seconds to run.')
        return results 
    return wrapper

这现在应该可以工作了。我们可以添加最后一个调整来保留函数的名称

事实上,装饰器将更改函数的名称。

@tictoc
def fat(num):
    f = 1
    for x in range(1, num+1):
        f *= x
    return f

print(fat.__name__)
#outputs wrapper

要解决此问题,请执行此操作


from functools import wraps


def tictoc(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        results = func(*args, **kwargs) 
        t2 = time.time()-t1 
        print(f'Took {t2} seconds to run.')
        return results 
    return wrapper

的输出不正确。print(fat.__name__)fat

编写装饰器的另一种方法是使用更易于阅读的类。


class tictoc:

    def __init__(self, func):
        self.func = func
        self.__name__ = func.__name__

    def __call__(self, *args, **kwargs):

        t1 = time.time()

        results = self.func(*args, **kwargs)
        t2 = time.time() - t1
        print(f'Took {t2} seconds to run.')
        return results

这应该如上所述,但自由度较小。例如,向装饰器添加参数。有了函数,我们就可以用三个嵌套函数来做到这一点

def tictoc(loops=1):
    def inner(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            
            t2_ = 0
            for loop in range(1, loops+1):
                print(f'{loop=}')
                t1 = time.time()
                results = func(*args, **kwargs)
                t2 = time.time() - t1
                t2_ += t2
                
                print(f'Took {t2} seconds to run')
            if loops > 1:
                print(f'Took average {t2_/loops} seconds to run')
                
            return results
    
        return wrapper
    return inner




@tictoc(3)
def fat(num):
    f = 1
    for x in range(1, num + 1):
        f *= x
    return f

print(fat(5))

还在琢磨如何用课堂;)

评论

1赞 Domenico Spidy Tamburro 9/25/2023
最初,您的答案看起来与我之前发布的答案相同。最终,您正在添加非常有趣的材料。荣誉。
0赞 Prayson W. Daniel 9/26/2023
我没有看到你的:(相反,我会编辑你的。