簡體   English   中英

如何理解python裝飾器參數傳遞

[英]How to understand python decorator arguments pass

我嘗試了解python裝飾器

def dec(func):
    def wrap(*args,**kw):
        print args, kw
        return func(*args,**kw)
    return wrap

@dec
def myfunc(a=1,b=2,c=3):
    return a+b+c


>>> myfunc()
() {}
6
>>> myfunc(1,2,3)
(1, 2, 3) {}
6
>>> myfunc(1,2,c=5)
(1, 2) {'c': 5}
8
>>> 

當我運行myfunc() args和kw什么都沒有,但是當我運行myfunc(1,2,3)myfunc(1,2,c=5) ,args和kw傳遞給dec函數。

我所知,

@dec
def myfunc(...)

等於myfunc = dec(myfunc) <-此處未提及任何參數。

如何將參數傳遞給dec wrap函數? 如何理解這些?

不知道我是否正確理解了您的問題,但是myfunc參數的默認值僅是myfunc知道的-您的裝飾器不了解它們,因此無法打印它們。

這就是為什么:

myfunc()

導致打印:

() {}

裝飾器的*args**kw均為空,但是在這種情況下,裝飾的函數將使用默認值。

在第二種和第三種情況下,您將打印出參數,因為它們已顯式傳遞給裝飾函數:

 def wrap(*args,**kw): <- this is what is actually called when you invoke decorated function, no default values here
    print args, kw
    return func(*args,**kw) <- this is the function you decorate
    #if func has default parameter values, then those will be used 
    #when you don't pass them to the wrapper but only **inside** func
 return wrap

編輯:看起來您會誤以裝飾功能調用裝飾功能:

myfunc = dec(myfunc) 

使用dec裝飾myfunc並等效於

@dec
def myfunc(...)

另一方面,在使用其中任何一個之后:

myfunc(whatever)

調用裝飾器中定義的wrap函數,后者將依次調用原始myfunc

裝飾器是函數包裝器。 他們提供了一個將原始代碼包裝到一些預處理和后期處理代碼中的函數,但仍需要調用原始函數(通常使用與沒有裝飾器時調用的參數相同的參數)。

Python有兩種類型的參數,即位置參數和關鍵字參數(這與裝飾器無關,這是通用的python基礎知識)。 *表示位置(內部是列表), **表示關鍵字(字典)參數。 通過指定兩者,您可以讓裝飾器接受所有可能的參數類型,並將其傳遞給基礎函數。 但是,調用合同仍由您的函數定義。 例如,如果僅接受關鍵字參數,則裝飾器函數通過位置參數傳遞時將失敗。

在您的特定示例中,您有一些預處理代碼(即將在調用原始函數之前運行的代碼)。 例如,在此代碼中,您可以打印出*args ,表明原始函數可能不接受所有參數,因為它不接受任何位置參數。

您不一定必須通過*args**kwargs 實際上,您可以定義一個裝飾器,該裝飾器根據傳入的參數來確定要提供給原始函數的參數,例如,

def dec(fun):
    def wrap(*args, **kwargs):
        if 'a' in kwargs:
            return fun(kwargs[a])
        else:
            return fun(*args)
    return wrap

另一種思考的方式是說:

def wrapper(some_function):
    def _inner(*args, **kwargs):
        #do some work
        return func(*args, **kwargs)
    return _inner

@wrapper
def foo(a, b, c):
    return "Foo called, and I saw %d, %d, %d" % (a, b, c)

...您得到的結果大致類似於以下內容:

def foo(a, b, c):
    #do some work
    return "Foo called, and I saw %d, %d, %d" % (a, b, c)

這並不完全正確,因為在實際的foo()調用之前#做 #do some work ,但是作為近似,這就是您所得到的。 出於這個原因,如果存在,包裝器就無法真正“查看” foo()的默認參數。 因此,考慮它的更好方法可能是:

#always execute this code before calling...
def foo(a, b, c):
    return "Foo called and I saw %d, %d, %d" % (a, b, c)

因此,一些基本的東西可能看起來像這樣。

def wrap(f):
...     def _inner(*a, **k):
...             new_a = (ar*2 for ar in a)
...             new_k = {}
...             for k, v in k.iteritems():
...                     new_k[k] = v*2
...             return f(*new_a, **new_k)
...     return _inner
... 
>>> def foo(a=2, b=4, c=6):
...     return "%d, %d, %d" % (a, b, c)
... 
>>> foo()
'2, 4, 6'
>>> foo(1, 5, 7)
'1, 5, 7'
>>> foo = wrap(foo)    #actually wrapping it here
>>> foo()
'2, 4, 6'
>>> foo(3, 5, 6)
'6, 10, 12'
>>> foo(3, 5, c=7)
'6, 10, 14'
>>> 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM