2011-02-04 58 views
1

我對具有默認參數的功能有一些疑問。
具有默認參數的功能問題

import sys 
from random import randint 

my_list = [1,2,3] 
def Veli(a, b = my_list): 
    my_list.append(randint(1,1500)) 
    print a + b[-1] 

print "Number of Refs:", sys.getrefcount(my_list) 
print "Def args:", Veli.func_defaults 
Veli(1) # This is 4 
Veli(1) # We almost always obtain different result because of randint 
Veli(1) # Again different results. 
print Veli.func_defaults 
print "Number of Refs:", sys.getrefcount(my_list) 
Veli(1) 
my_list = [-5] # old my_list has different address 
print "Number of Refs:", sys.getrefcount(my_list) # Less than previous 
print "Def args:", Veli.func_defaults 
Veli(1) # Now gives same results. 
print "Def args:", Veli.func_defaults 
Veli(1) # Same result again... 


輸出:(一些數字取決於哪些值randint返回當然。)

編號參考文獻的:3個
防守ARGS:([1, 2,3]])
([1,2,3,321,1118,739],)
編號參考文獻的:3
參考文獻號:2
防守ARGS:([1,2,3,321,1118 ,739,302],)
防守ARGS:([1,2,3,321,1118,739,302],)


下面的代碼給出了一個比前一個小的數字,因爲b和my_list不再被引用到相同的地址。

print "#ref:", sys.getrefcount(my_list) # Less than previous 

現在,我們有辦法達到B,該函數的默認參數(的唯一途徑?):

Veli.func_defaults[0] 


對不起我的長篇解釋。這裏是我的問題:

  1. 這是問題嗎?我的模塊字典粉碎my_list變量,然後現在my_list變量的默認地址比以前。然後,使用名爲my_list的全局變量的函數正在改變(增長),而我的默認參數不是。當a + b[-1]表達式被執行時,爲什麼b看不到全局變量my_list?我知道,b與my_list有不同的地址(因爲mutual objects (like list) are guaranteed to refer to different, unique, newly created list),但是爲什麼Python實現,因此b在b是函數參數時看不到全局變量?你能全面解釋一下嗎?

  2. 有什麼辦法可以得到與Veli.func_defaults [0]相同的結果嗎?這段代碼執行然後說,我想改變我的名爲Veli的函數的默認參數。我不能用my_list來做這件事,因爲my_list和b有不同的地址。一種方法是更改​​列表中的元素Veli.func_defaults [0]。有什麼不同的方法嗎?

  3. (它與上面的代碼沒有太大關係)如何獲取變量的地址?例如,如何獲取地址b?我使用內置函數,如__hash__,但應該有更合適的方法。


注:

一)該代碼可能是無用的任何原因,但我想學的觀點。
b)中的Python 2.6.6(r266:84292,2010年9月15日,15時52分39秒)
[4.4.5 GCC]上linux2上

+1

http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default -argument – ncoghlan 2011-02-04 14:46:24

+0

@ncoghlan:Upvoted,因爲它是重要的演講,但由於OP利用這種行爲,在這裏不完全相關。 – delnan 2011-02-04 14:52:52

回答

1
  1. 默認參數是在函數定義時間界限。行def Veli(a, b = my_list):提到對象my_list碰巧指當時轉換成func_defaults。 Python 從不使用傳遞引用 - 變量保存引用,並且這些引用總是按值傳遞。因此,實際上保存在b中的參考(指針)被複制,並且沒有人記得它來自哪裏或者更新它。
  2. 不太清楚你在問什麼,請澄清。如果沒有第二個參數被傳遞,b將會是Veli.func_defaults[0],但是如果它被傳遞,顯然是不同的(當然,除非調用者訪問func_defaults ...你應該假設他沒有)。如您在其他答案中所建議的那樣,您可以將b默認爲None,並使用全球my_list如果​​- 這會在每次調用時爲您提供全新的參考副本。也許你應該寫一個班級,並保留默認值作爲self的屬性,並應用通常的成語(... = None): if ... is None: use = default
  3. 內置函數id。實際上,它的實現定義了它返回的內容(不一定是地址),只要它是一個表示對象標識的整數,即不同的對象(具有重疊的生命週期)具有不同的id,並且同一個對象總是給出相同的id在其一生中。 CPython選擇的簡單返回值就是地址。
1

我建議你看看this existing question,因爲你的問題本質上與同一問題有關(儘管對主題的看法略有不同)。

如您所知,當執行功能定義時,b必定指向my_list。當您稍後將my_list更改爲指向其他對象時,b保持不變。

得到「延遲綁定」你想寫你的函數是這樣的:

def Veli(a, b=None): 
    if b is None: 
     b = my_list # Gets value of my_list at call time 
    my_list.append(randint(1,1500)) 
    print a + b[-1]