2012-04-12 105 views
5

我想很多人都看到了python的函數接收默認參數。例如:Python函數中的默認參數在哪裏

def foo(a=[]): 
    a.append(3) 
    return a 

如果我們使用FOO()調用此函數,輸出將在調用後每次追加整數「3」。

定義此函數時,會在當前環境中定義一個名爲'foo'的函數對象,此時也會評估默認參數值。每次在沒有參數的情況下調用函數時,評估參數值將根據代碼進行更改。

我的問題是,這個評估參數在哪裏存在? 它是在函數對象中還是在調用函數時在方法對象中? 因爲python中的所有東西都是一個對象,所以必須有一些地方來保存'a' - >評估參數的名稱 - >值綁定。我是否過度思考這個問題?

回答

10

正如其他人已經說過的,默認值存儲在函數對象中。

例如,在CPython中,你可以這樣做:

>>> def f(a=[]): 
...  pass 
... 
>>> f.func_defaults 
([],) 
>>> f.func_code.co_varnames 
('a',) 
>>> 

然而,co_varnames可能含有比args來名的詳細所以還需要進一步的處理和這些屬性甚至可能不存在於其他Python實現。因此,你應該使用inspect模塊,而不是它負責所有的工作細節爲您提供:

>>> import inspect 
>>> spec = inspect.getargspec(f) 
>>> spec 
ArgSpec(args=['a'], varargs=None, keywords=None, defaults=([],)) 
>>> 

ArgSpec是一個名爲元組這樣你就可以訪問所有值屬性:

>>> spec.args 
['a'] 
>>> spec.defaults 
([],) 
>>> 

由於documentation說,defaults元組總是對應於args中最後n個參數。這給你你的映射。

要創建一個字典,你可以這樣做:

>>> dict(zip(spec.args[-len(spec.defaults):], spec.defaults)) 
{'a': []} 
>>> 
4

它連接到函數對象,見foo.func_defaults

>>> foo() 
>>> foo.func_defaults 
([3],) 
>>> foo() 
>>> foo.func_defaults 
([3, 3],) 

在,如果你想獲得的a映射到[]情況下,您可以訪問foo.func_code

defaults = foo.func_defaults 
# the args are locals in the beginning: 
args = foo.func_code.co_varnames[:foo.func_code.co_argcount] 
def_args = args[-len(defaults):] # the args with defaults are in the end 
print dict(zip(def_args, defaults)) # {'a': []} 

(但是,顯然,犛牛的版本更好。)

+2

是的,但OP所要求的'a'到'[]'的映射在哪裏? – MattH 2012-04-12 09:47:35

+0

@MattH更新了答案,謝謝。 – bereal 2012-04-12 10:10:05

4

它在函數對象中,在func_defaults

def f(a=[]): a.append(3) 

print f.func_defaults # ([],) 

f() 

print f.func_defaults # ([3],) 
+2

是的,但OP所要求的a到[]的映射在哪裏? – MattH 2012-04-12 09:47:56

2

它存儲在函數對象的func_defaults屬性中。

>>> foo.func_defaults 
([],) 
>>> foo() 
([3],) 
1

我發現一個有趣的情況:在Python 2.5。版本2,嘗試函數 '富()'

>>> foo() 
[1] 
>>> foo() 
[1] 
>>> foo() 
[1] 

因爲調用的函數的對象是不同的:

>>> id(foo()) 
4336826757314657360 
>>> id(foo()) 
4336826757314657008 
>>> id(foo()) 
4336826757314683160 

在2.7.2版本:

>>> foo() 
[1] 
>>> foo() 
[1, 1] 
>>> foo() 
[1, 1, 1] 

在這種情況下,每次調用該功能的對象都是一樣的:

>>> id(foo()) 
29250192 
>>> id(foo()) 
29250192 
>>> id(foo()) 
29250192 

這是不同版本的問題嗎?

+0

不應該發生。確保你在兩個版本上都使用*完全相同的功能。 – yak 2012-04-12 21:59:38