python參數傳遞*args和**kwargs
和*
實際上真正的Python參數傳遞語法是 *
和 **
。*args
和 **kwargs
只是一種約定俗成的編程實踐。我們也可以寫成 *vars
和 **kvars
。就如同其他常規變量的命名一樣, args
和 kwargs
只是一種習慣的名稱。
*args
和 **kwargs
一般是用在函數定義的時候。二者的意義是允許定義的函數接受任意數量的參數。也就是說我們在函數被調用前并不知道也不限制將來函數可以接收的參數數量。在這種情況下我們可以使用 *args
和 **kwargs
。
接下來我們分別分析 *args
和 **kwargs
在函數定義時(即在形參列表中)和在函數調用時的用法。
在函數定義時的*和**(即函數形參列表中的*和**)
在函數定義時的*args
*args
用來表示函數接收可變長度的非關鍵字參數列表作為函數的輸入。我們通過一個例子來體會參數列表中的 *args
的用法:
def test_args(normal_arg, *args):print("normal arg:" + normal_arg)print(type(args))for arg in args:print("another arg through *args :" + arg)test_args("normal", "Python", "C", "C++")
輸出:
normal arg:normal
<class 'tuple'>
another arg through *args :Python
another arg through *args :C
another arg through *args :C++
在函數定義時的**kwargs
**kwargs
表示函數接收可變長度的關鍵字參數字典作為函數的輸入。當我們需要函數接收帶關鍵字的參數作為輸入的時候,應當使用 **kwargs
。我們可以通過以下這個例子來進一步理解 **kwargs
。
def test_kwargs(**kwargs):if kwargs is not None:print(type(kwargs))for key, value in kwargs.items():print("{} = {}".format(key,value))test_kwargs(name="python", value="5")
輸出:
<class 'dict'>
name = python
value = 5
如何處理關鍵字參數中的未知性
雖說參數列表中的 *和**使得我們可以接收任意多個參數變量,但是我們在設計函數時肯定也不是隨便什么參數都去處理的,函數的調用者應該明白在 *args
和 **kwargs
中需要傳遞什么參數,可以傳遞什么參數(比如深度學習模型中的模型結構的設置,如模型深度,dropout概率等)。并且我們在函數體中來處理這些參數。
然而,我們并不知道調用者在調用時會傳什么,哪些又應該在調用時使用默認值。這里筆者參考了 timm
視覺庫對 ViT 的實現。
在_create_vision_trainsformer 函數中,我們只要處理和我們相關的傳入參數:
def _create_vision_transformer(variant, pretrained=False, default_cfg=None, **kwargs):default_cfg = default_cfg or default_cfgs[variant]if kwargs.get('features_only', None):raise RuntimeError('features_only not implemented for Vision Transformer models.')# NOTE this extra code to support handling of repr size for in21k pretrained modelsdefault_num_classes = default_cfg['num_classes']num_classes = kwargs.get('num_classes', default_num_classes)repr_size = kwargs.pop('representation_size', None)if repr_size is not None and num_classes != default_num_classes:# Remove representation layer if fine-tuning. This may not always be the desired action,# but I feel better than doing nothing by default for fine-tuning. Perhaps a better interface?_logger.warning("Removing representation layer for fine-tuning.")repr_size = Nonemodel = build_model_with_cfg(VisionTransformer, variant, pretrained,default_cfg=default_cfg,representation_size=repr_size,pretrained_filter_fn=checkpoint_filter_fn,pretrained_custom_load='npz' in default_cfg['url'],**kwargs)return model
可以看到,我們傳入的 kwargs
是一個字典,這里使用了字典的 get
、pop
兩種方法來處理。
python字典的get和pop方法
pop
dict.pop(key[,default])
刪除字典給定鍵 key 及對應的值,返回值為被刪除的值,若 key 不存在,則返回 default 參數所指定的默認值。
get
dict.get(key, default=None)
函數返回指定鍵的值,若 key 不存在,則返回 default 參數所指定的默認值。
通過這兩種方法,我們就能夠實現在 kwargs 中沒有傳入參數時使用默認值,而在有指定參數時,使用傳入值。
在函數調用時的*和**
-
列表前面加星號作用是將列表解開成兩個獨立的參數,傳入函數,
-
字典前面加兩個星號,是將字典解開成獨立的元素作為形參。
如果我們有一個加法函數 add()
:
def add(a, b):return a + b
函數調用時的*
我們可以這樣調用它:先構造一個列表,在用 * 將列表解開,將其中的值作為參數逐個傳入函數的參數列表:
data_list = [1, 2]
print(add(*data_list))
# 輸出:3
就相當于
print(add(1, 2))
我們甚至可以直接打印出 * 解開列表之后的參數序列:
print(*data_list)
# 輸出:1 2
函數調用時的**
我們還可用字典的形式來調用它:構造一個字典,鍵名為要調用函數的形參名稱,值為我們要傳遞的參數:
data_dict = {'a': 1, 'b': 2}
print(add(**data_dict))
# 輸出:3
相當于:
print(add(a=1, b=2))
注意這里我們構造的字典的鍵名必須對應函數形參列表中的形參名稱,否則匯報錯,比如
data_dict = {'a': 1, 'c': 2}
print(add(**data_dict))
相當于:
print(data(a=1, c=2))
將會報錯:
TypeError: add() got an unexpected keyword argument 'c'
兩種使用方式的統一理解
以關鍵字參數 **kwargs
為例,我們這樣考慮:
我們提到過,對于一個字典 dict={'a': 1, 'b': 2}
,給它加上 **
號 ,相當于是將其中的元素解開單拎出來,即 **dict
相當于 a=1, b=2
,這正好符合我們調用函數傳遞參數時的寫法。
而當 **kwargs
在中形參列表中時,我們傳入 a=1, b=2
,看一下,正好對應我們上面的 **dict
(只是名字不一樣,開始提到過,這無妨),那也把 **
號去掉后,kwargs
就相當于我們上面的 dict
,是一個字典:{'a': 1, 'b': 2}
,這就說明了為什么我們上面介紹函數定義時的 **kwargs
的例子中,type(kwargs)
為 dict
,并且在函數體中我們也是通過調用字典類的方法(如 items
、pop
、get
)來處理它。
Ref:
https://www.jianshu.com/p/be92113116c8
https://github.com/rwightman/pytorch-image-models