本文轉載自:http://www.pythoner.com/13.html
Python中將兩個字典進行合并操作,是一個比較常見的問題。本文將介紹幾種實現兩個字典合并的方案,并對其進行比較。
對于這個問題,比較直觀的想法是將兩個字典做相加操作,賦值給結果字典,其代碼為:
方法一:
1
dictMerged1
dict
( dict1.items()
dict2.items() )
然而,該方法合并時所用時間較長,效率更高的代碼為:
方法二:
1
dictMerged2
dict
( dict1,
dict2 )
這種方法使用的是dict()
工廠方法(Python2.2以上版本)。如果輸入參數是另一個字典(此處為dict1),則調用該工廠方法時會從dict1中復制內容生成新的字典。該工廠方法從Python2.3版本開始,允許接受字典或關鍵字參數字典進行調用。但應當注意,對于這種調用方式,dict()
最多只接受一個參數(或者說是一組name=value
的可變長參數),而不會再接受另一個字典。因此直觀上的簡單使用dict1與dict2兩個參數的方法會提示如下錯誤:
1
2
3
4
dictMerged
=
dict
( dict1, dict2 )
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
TypeError:
dict
expected at most
1
arguments, got
2
這也就是我們看到上面的方法2中使用的是dict2的原因。熟悉C的朋友應當注意,在這里的意思并不代表指針,這是Python中可變長函數參數的寫法(關于可變長函數參數的相關知識見下文)。在這里,的意思是基于字典的可變長函數參數。*
方法2執行的是如同下面方法3中的代碼,即先將dict1拷貝給dictMerged,在執行update()
操作:
1
2
dictMerged3
dict1.copy()
dictMerged3.update( dict2 )
對于第一步的復制操作而言,這種使用內建方法copy()
的復制方式,和方法2中的復制結果是一樣的,但根據《Core Python Programming (2nd edition)》一書中7.3.2節所述,從已存在字典中生成新字典的方式dictNew = dict( dictOld )
較內建方法dictNew = dictOld.copy()
會慢一些,因此書中推薦使用copy()
方法。
因此,從這幾種方式看來,方法3的效率最高,并且代碼也比較易讀。
Python可變長度的函數參數
在編程的過程中,我們可能會遇到函數參數個數不固定的情況。這時就需要使用可變長度的函數參數來實現我們的功能。在Python中,有兩種變長參數,分別是元組(非關鍵字參數)和字典(關鍵字參數)。其調用方式是:func( *tuple_grp_nonkw_args, *dict_grp_kw_args )
,下面將詳細介紹這兩種變長參數。
1.元組變長參數
當函數調用中包括一個元組變長參數tuple_grp_nonkw_args時,除去前面固定位置參數和關鍵字參數的其余參數將按順序插入一個元組進行訪問,這和C語言中的varargs的功能相同。
假設有這樣一個函數(其中,positional_arg是位置固定的標準調用參數,keyword_arg是關鍵字參數):
示例:
1
2
3
4
5
def
foo( positional_arg, keyword_arg
=
'default'
,
tuple_arg ):
print
"positional arg: "
, positional_arg
print
"keyword_arg: "
, keyword_arg
for
each_additional_arg
in
tuple_arg:
print
"additional_arg: "
, each_additional_arg
我們使用一些示例來了解它是怎么工作的:
foo(
1
)
positional arg:
1
keyword_arg: default
foo(
1
,
2
)
positional arg:
1
keyword_arg:
2
foo(
1
,
2
,
3
)
positional arg:
1
keyword_arg:
2
additional_arg:
3
1
2
3
4
5
6
7
8
9
10
11
foo(
1
,
2
,
3
,
4
,
5
,
6
)
positional arg:
1
keyword_arg:
2
additional_arg:
3
additional_arg:
4
additional_arg:
5
additional_arg:
6
foo(
1
,
2
,(
3
,
4
,
5
,
6
))
positional arg:
1
keyword_arg:
2
additional_arg: (
3
,
4
,
5
,
6
)
2.字典變長參數
既然Python中允許關鍵字參數,那么也應該有一種方式實現關鍵字的變長參數,這就是字典變長參數。
字典變長參數中,額外的關鍵字參數被放入了一個字典進行使用。字典中,鍵為參數名,值為相應的參數值。其表示方式是放在函數參數最后的開頭的參數,如dict_grp_kw_args。(需要注意的是,**被重載以不與冪運算混淆。)
以下是一個字典變長參數的示例函數:
1
2
3
4
5
def
foo( positional_arg, keyword_arg
=
'default'
,
dict_arg ):
print
"positional arg: "
, positional_arg
print
"keyword_arg: "
, keyword_arg
for
each_dict_arg
in
dict_arg.keys():
print
"dict_arg: %s=>%s"
%
( each_dict_arg,
str
( dict_arg[each_dict_arg] ) )
下面是一段演示結果:
1
2
3
4
foo(
1
,
2
, a
=
"b"
)
positional arg:
1
keyword_arg:
2
dict_arg: a
b
3.注意
函數調用的完整表達形式為:func( positional_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args )
在使用的過程中,所有參數都是可選的,但應當注意的是:上面四種參數的位置是不可調換的!
4.擴展:C語言中的變長參數
作為一個學藝不精的人,之前一直不知道C語言中也是有可變參數的,直到在《Pointers on C》(中譯名:《C和指針》,人民郵電出版社)中看到相關內容(7.6節)。
4.1 stdarg宏
在C語言中,可變參數是通過stdarg宏來實現的,它是標準庫的一部分。這個頭文件聲明了一個類型va_list
和三個宏va_start
、va_arg
、va_end
。我們可以聲明一個類型為va_list的變量,與三個宏配合使用,訪問參數的值。
下面是一個計算多個數值平均值的示例函數:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//下面是一個計算多個數值平均值的示例函數:
include <stdarg.h>
float
avg(
int
n, ... ) {
va_list
var_arg;
float
sum = 0;
// 準備訪問變長參數
va_start
( var_arg, n );
// 添加取自變長參數列表的值
for
( i = 0; i < n; i += 1) {
sum +=
va_arg
( var_arg,
int
);
}
// 完成處理變長參數
va_end
( var_arg);
return
sum / n;
}
其中,函數參數中的...
作為參數占位符,代表數量和類型不可知的一些參數。
函數中聲明了一個va_list類型的變量var_arg用于訪問參數列表的不確定部分。這個變量通過調用va_start進行初始化,其中,第一個參數是va_list變量的名字,第二個參數是占位符前最后一個有名字的參數。初始化過程將var_arg變量指向可變參數中的第一個參數。
va_list的使用中,包括兩個參數,第一個參數是va_list變量,第二個參數是下一個參數的類型。本例中假設輸入數據均為整型,因此均設置為int,而在一些情況下,下一個參數的類型會由之前的參數來決定。
最后,調用va_end結束變長參數的訪問。
4.2 限制與注意事項
可變參數是從頭到尾進行訪問的,即可以在訪問了數個參數之后結束,但不可以一開始就訪問中間的參數。
另外,由于可變參數部分沒有原型,因此作為可變參數傳遞給函數的值都做了缺省的函數類型提升。
從va_start的調用可以看出,如果使用可變參數必須有至少一個確定的參數,否則無法使用va_start。
對于這些宏,有兩個基本限制:其一,無法判斷實際存在的參數數量;其二,不能判斷參數類型。
還需要注意的是,一旦在使用中寫錯下一個參數的類型,后果可能不堪設想。