2012-09-13 71 views
3

弄得我有一些測試代碼:蟒蛇,在裝修和關閉

 
def num(num): 
    def deco(func): 
     def wrap(*args, **kwargs): 
      inputed_num = num 
      return func(*args, **kwargs) 
     return wrap 
    return deco 


@num(5) 
def test(a): 
    return a + inputed_num 

print test(1) 

當運行這段代碼,我得到了一個錯誤顯示,「inputed_num」不是定義

我的問題是: 在包裝函數中,是否沒有func可以得到'inputed_num'的閉包?

無論如何,如果不是,我該怎麼做才能得到我的目標:初始化某個值,並直接在主函數中使用該值。

認爲。

回答

5

我的問題是:在包裝功能,是不是有這FUNC能搞到「inputed_num」封閉?

對不起,這不是裝飾者的工作方式。它們在之後被應用該功能被初始定義。屆時,爲時已晚。

當你寫:

@num(5) 
def test(a): 
    return a + inputed_num 

也就是相當於:

def test(a): 
    return a + inputed_num 

test = num(5)(test)  # note that num(5) is called after test() is defined. 

爲了實現自己的目標,讓inputed_num是第一個參數測試。然後,有這樣的說法你的裝飾通:

def num(num): 
    def deco(func): 
     def wrap(*args, **kwargs): 
      inputed_num = num 
      return func(inputed_num, *args, **kwargs) # this line changed 
     return wrap 
    return deco 

@num(5) 
def test(inputed_num, a):        # this line changed 
    return a + inputed_num 

@num(6) 
def test2(inputed_num, a): 
    return a + inputed_num 

print test(10) # outputs 15 
print test2(10) # outputs 16 

希望掃清了一切爲你:-)

+0

只是因爲該函數定義並不一定意味着它是爲時已晚後的裝飾應用。直到函數被實際調用時纔會引發錯誤,並且由於它是被調用的函數,因此它可以操作全局變量,以在調用原函數時改變原函數的行爲。 – BrenBarn

+0

OP沒有要求一個全局變量 - 這會有它自己的問題(特別是,如果他多次使用裝飾器)。相反,他想要一個閉包(a.k.a是一個單元變量)。 Python的語義要求在最初定義* test()*的時候靜態引用單元變量(詞彙範圍)。之後運行裝飾器時,創建單元變量實在太晚了。 –

+0

認爲。在閱讀您的文章後,我明白了我的錯誤。 **但是**我不想做一些主要功能的修改 – Yueyoum

5

不,不是那樣的封閉。函數可以關閉周圍詞彙上下文中存在的變量,而不是在調用上下文中。換句話說,如果你真的在另一個寫一個函數,那麼內一個可以訪問的變量外一個:

def f(): 
    g = 2 
    def f2(): 
     print g 
    f2() 

但功能從來沒有訪問到調用它們的函數內部變量。

一般來說,沒有辦法做你想做的事,即在函數外部設置一個函數中的任意變量。最接近的是,你可以在裝飾器中使用global inputed_numinputed_num分配爲全局變量。然後test將訪問全局值。

def num(num): 
    def deco(func): 
     def wrap(*args, **kwargs): 
      global outsider 
      outsider = num 
      return func(*args, **kwargs) 
     return wrap 
    return deco 
@num(5) 
def test(a): 
    print a+outsider 

>>> test(2) 
7 

但當然變量設置,然後全球化的,多個併發應用(例如,遞歸)是行不通的。 (只是爲了好玩,你也可以看到here這樣做的一個非常神祕的方式,但它太瘋狂了,在真實世界中有用。)

+0

認爲,正如你所提到的,這段代碼的確運行併發。 :) – Yueyoum

+0

使用一個全局變量在多個不同的函數之間進行通信遠不是最佳實踐。除非沒有別的方法可行,否則我不會推薦它。 –

0

這不是裝飾的方法應該是使用,我覺得你的目的可能與此

def outwrapper(n): 
    def wrapper(func): 
     def f(*args, **argw): 
      return n + func(*args, **argw) 
     return f 
    return wrapper 


@outwrapper(4) 
def test(n): 
    return n 


print test(1) 
+0

**不是這個**代碼只是一個例子。但還是謝謝:) – Yueyoum

2

去做的,@Raymond所說的那樣 - 函數定義後的裝飾應用。這意味着,在編譯函數體本身時,Pythn會看到inputed_num變量,並且當它僞裝一個本地定義的變量時,它會生成代碼以嘗試將其作爲全局變量來訪問它。

這意味着你可以在你的裝飾器中爲它做一個解決方法: 你的裝飾器可以在函數globals()空間中用所需的名字設置一個全局變量,然後調用該函數。它應該在單線程代碼可靠地工作:

def num(num): 
    def deco(func): 
     def wrap(*args, **kwargs): 
      glob = func.func_globals 
      marker = object() 
      original_value = glob.get("inputed_num", marker) 
      glob["inputed_num"] = num 
      result = func(*args, **kwargs) 
      if original_value is marker: 
       del glob["inputed_num"] 
      else: 
       glob["inputed_num"] = original_value 
      return result 
     return wrap 
    return deco 


@num(5) 
def test(a): 
    return a + inputed_num 

和:

>>> print test(1) 
6 
+0

酷!但也許不符合我的需求。這個裝飾將服務於不同的功能,並運行合併。想 :) – Yueyoum