鍍金池/ 問答/Java  PHP  Python  Ruby  HTML/ python裝飾器傳參問題

python裝飾器傳參問題

問題描述

自己寫了個帶參數(shù)的裝飾器,現(xiàn)在需要裝飾器隨著不同條件使用不同的參數(shù)。

問題出現(xiàn)的環(huán)境背景及自己嘗試過哪些方法

自己寫了個帶參數(shù)的裝飾器,現(xiàn)在需要裝飾器隨著不同條件使用不同的參數(shù)。
如下,利用一個for循環(huán)將不通的參數(shù)傳進裝飾器(這種時候是可以使用的):

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()

但是現(xiàn)在假設(shè)我的裝飾器有很多參數(shù),而且for循環(huán)內(nèi)部也有很多函數(shù),我不想每一個裝飾器都寫那么多參數(shù),于是想先給裝飾器傳好參數(shù),但是確報錯了如下:

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

可以看到很怪的是第一個循環(huán)的時候是可以用的有輸出,但是到了第二個for循環(huán)的時候裝飾器傳參卻出現(xiàn)了錯誤,不知道是為什么?請問到底錯在了那里?謝謝!

更奇怪的是,我換了個函數(shù)別名竟然就可以用了。。。,代碼如下:

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()

請大神解釋到底是為什么???謝謝!

回答
編輯回答
乞許

“帶參數(shù)的裝飾器”,這樣的描述并不準確,@desc(arg) 更好的理解是函數(shù) desc 被調(diào)用,該函數(shù)返回一個裝飾器。況且你已經(jīng)知道處理方法了,只是想要知道為什么。這點上是一點就通的。

先理解一個,函數(shù)允許重新賦值的

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

然后你對裝飾器的理解也已經(jīng)很充分了:

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

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

這里的原因在于,你把 log 重新賦值了,它變成了一個裝飾器,而不是一個返回裝飾器的函數(shù)。嗯,原因就是這么簡單。

2017年8月2日 14:34