python装饰器传参问题

问题描述

自己写了个带参数的装饰器,现在需要装饰器随着不同条件使用不同的参数。

问题出现的环境背景及自己尝试过哪些方法

自己写了个带参数的装饰器,现在需要装饰器随着不同条件使用不同的参数。
如下,利用一个for循环将不通的参数传进装饰器(这种时候是可以使用的):

from functools import wraps


def log(a, b, c):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('hello', str(a), str(b), str(c), func.__name__)
            func(*args, **kwargs)
            print('goodbye', str(a), str(b), str(c), func.__name__)
        return wrapper
    return decorator


a_l, b_l, c_l = [1, 2, 3], [4, 5, 6], [7, 8, 9]
l = list(range(5))

for i, j, k in zip(a_l, b_l, c_l):
    @log(i, j, k)
    def f():
        print('ok')
    f()

但是现在假设我的装饰器有很多参数,而且for循环内部也有很多函数,我不想每一个装饰器都写那么多参数,于是想先给装饰器传好参数,但是确报错了如下:

from functools import wraps


def log(a, b, c):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('hello', str(a), str(b), str(c), func.__name__)
            func(*args, **kwargs)
            print('goodbye', str(a), str(b), str(c), func.__name__)
        return wrapper
    return decorator


a_l, b_l, c_l = [1, 2, 3], [4, 5, 6], [7, 8, 9]
l = list(range(5))

for i, j, k in zip(a_l, b_l, c_l):
    log = log(i, j, k)
    @log
    def f():
        print('ok')
    f()
    
#报错
hello 1 4 7 f
ok
goodbye 1 4 7 f
Traceback (most recent call last):
  File "D:\test\ttt.py", line 22, in <module>
    log = log(i, j, k)
TypeError: decorator() takes 1 positional argument but 3 were given

可以看到很怪的是第一个循环的时候是可以用的有输出,但是到了第二个for循环的时候装饰器传参却出现了错误,不知道是为什么?请问到底错在了那里?谢谢!

更奇怪的是,我换了个函数别名竟然就可以用了。。。,代码如下:

from functools import wraps


def log(a, b, c):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('hello', str(a), str(b), str(c), func.__name__)
            func(*args, **kwargs)
            print('goodbye', str(a), str(b), str(c), func.__name__)
        return wrapper
    return decorator


a_l, b_l, c_l = [1, 2, 3], [4, 5, 6], [7, 8, 9]
l = list(range(5))

for i, j, k in zip(a_l, b_l, c_l):
    lg = log(i, j, k)   # 把原来的log,换成了lg就可以正常运行了

    @lg
    def f():
        print('ok')
    f()

请大神解释到底是为什么???谢谢!

阅读 3k
1 个回答

“带参数的装饰器”,这样的描述并不准确,@desc(arg) 更好的理解是函数 desc 被调用,该函数返回一个装饰器。况且你已经知道处理方法了,只是想要知道为什么。这点上是一点就通的。

先理解一个,函数允许重新赋值的

def f():
    pass
f = 1
print(f)    # 1   

然后你对装饰器的理解也已经很充分了:

@log(i, j, k)
    def f():

# 等价于
tmp = log(i,j,k)
@tmp
def f():

这里的原因在于,你把 log 重新赋值了,它变成了一个装饰器,而不是一个返回装饰器的函数。嗯,原因就是这么简单。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题