2017-04-04 86 views
2

如何定義關鍵字參數字典的初始內容?下面是一個醜陋的解決方案:初始化python中的默認關鍵字參數列表

def f(**kwargs): 
    ps = {'c': 2} 
    ps.update(kwargs) 
    print(str(ps)) 

預期的行爲:

f(a=1, b=2) => {'c': 2, 'a': 1, 'b': 2} 
f(a=1, b=2, c=3) => {'c': 3, 'a': 1, 'b': 2} 

然而,我希望做更多的東西一點點的線路:

def f(**kwargs = {'c': 2}): 
    print(str(kwargs)) 

甚至:

def f(c=2, **kwargs): 
    print(str(???)) 

想法?

+0

你的第一種方法是不夠好。 – hallazzang

+0

檢查[請求庫](https://git.io/vSRf8),它使用相同的方法。 – hallazzang

回答

0

你試過:

def f(c=2, **kwargs): 
    kwargs['c'] = c 
    print(kwargs) 

更新

除非,你可以使用inspect訪問代碼的對象,並從得到的唯一關鍵字指定參數時,甚至全部ARGS :

import inspect 

def f(a,b,c=2,*,d=1,**kwargs): 
    code_obj = inspect.currentframe().f_code 
    nposargs = code_obj.co_argcount 
    nkwargs = code_obj.co_kwonlyargcount 
    localvars = locals() 
    kwargs.update({k:localvars[k] for k in code_obj.co_varnames[:nposargs+nkwargs]}) 

    print(kwargs) 

g=f 

g('a', 'b') 
+0

我不喜歡參數列表和'kwargs'字典更新中'c'的重複。這個列表可能很長(...) –

+0

你問了它... –

1

您對Python 3.5+的第一種方法可能的變體是定義默認值並展開提供的在一行上,這也可以讓你在同一行上取代kwargs參數,例如:

def f(**kwargs): 
    # Start with defaults, then expand kwargs which will overwrite defaults if 
    # it has them 
    kwargs = {'c': 2, **kwargs} 
    print(str(kwargs)) 

的另一種方法(即不會產生相同的字符串)創建其行爲使用collections.ChainMap相同的方式映射( 3.3+):

from collections import ChainMap 

def f(**kwargs): 
    # Chain overrides over defaults 
    # {'c': 2} could be defined outside f to avoid recreating it each call 
    ps = ChainMap(kwargs, {'c': 2}) 
    print(str(ps)) 

就像我說的,是不會產生相同的字符串輸出,但不像其他的解決方案,它不會變得越來越昂貴爲通過關鍵字參數的數量增加(其根本不需要複製它們)。

+0

當然'print(str(defaults))'可以用'print(defaults)'代替所有上述內容,但是它不會已經結束幾乎相同的OP –

+0

哎呀評論錯誤的帖子大聲笑 –

3

首先與您當前的解決方案來解決這些問題:

def f(**kwargs): 
    ps = {'c': 2} 
    ps.update(kwargs) 
    print(str(ps)) 

這將創建一個新的字典,然後必須花時間與kwargs所有的值更新它。如果kwargs很大,可能相當低效,正如你指出的那樣有點醜陋。

顯然你的第二個是無效的。

至於第三個選項中,一個實現已經由Austin Hastings

如果您正在使用默認值kwargs而不是關鍵字參數給出有可能是一個原因(或者至少應該有;例如一界面,其定義了c而沒有明確要求ab可能不被期望,即使該實現可能需要值爲c)。

簡單的執行採取的dict.setdefault優勢,以更新kwargs的值當且僅當關鍵是不存在:

def f(**kwargs): 
    kwargs.setdefault('c', 2) 
    print(str(kwargs)) 

現在,作爲在之前的評論中提及了由OP,名單默認值可能是相當長的,在這種情況下,你可以有一個循環設置默認值:

def f(**kwargs): 
    defaults = { 
     'c': 2, 
     ... 
    } 
    for k, v in defaults.items(): 
     kwargs.setdefault(k, v) 
    print(str(kwargs)) 

一對夫婦的性能問題在這裏。首先defaults字典在函數的每次調用時都會被創建。這可由該函數的外部移動的默認值等從而加以改進:

DEFAULTS = { 
    'c': 2, 
    ... 
} 
def f(**kwargs): 
    for k, v in DEFAULTS.items(): 
     kwargs.setdefault(k, v) 
    print(str(kwargs)) 

其次在Python 2,dict.items返回(鍵,值)的副本對這樣反而dict.iteritemsdict.viewitems允許你迭代內容並因此更高效。在Python 3中,'dict.items`是一個視圖,所以這裏沒有問題。

DEFAULTS = { 
    'c': 2, 
    ... 
} 
def f(**kwargs): 
    for k, v in DEFAULTS.iteritems(): # Python 2 optimization 
     kwargs.setdefault(k, v) 
    print(str(kwargs)) 

如果效率和兼容性都擔心,你可以使用six庫的兼容性如下:

from six import iteritems 

DEFAULTS = { 
    'c': 2, 
    ... 
} 
def f(**kwargs): 
    for k, v in iteritems(DEFAULTS): 
     kwargs.setdefault(k, v) 
    print(str(kwargs)) 

此外,在for循環的每次迭代中,的setdefault方法的查找需要執行kwargs。如果你真的有一個非常大的數量的默認值的微優化的方法分配給一個變量,以避免重複查找:

from six import iteritems 

DEFAULTS = { 
    'c': 2, 
    ... 
} 
def f(**kwargs): 
    setdefault = kwargs.setdefault 
    for k, v in iteritems(DEFAULTS): 
     setdefault(k, v) 
    print(str(kwargs)) 

最後,如果默認值的數量,而不是預計會比大kwargs的數量,用kwargs更新默認值可能會更高效。爲此,您不能使用全局默認值,或者每次運行該函數時都會更新默認值,因此需要將默認值移回到函數中。這將使我們有以下幾點:

def f(**kwargs): 
    defaults = { 
     'c': 2, 
     ... 
    } 
    defaults.update(kwargs) 
    print(str(defaults)) 

享受:d

+0

當然,'print(str(defaults))'可以用'print(defaults)'代替上述所有內容,但它不會有與OP完全相同 –