當前位置:
首頁 > 知識 > 字典對象的 Pythonic 用法(上篇)

字典對象的 Pythonic 用法(上篇)

點擊

上方藍字

,快速關注我們)




編譯:伯樂在線 - 劉志軍


foofish.net/python-dict-usage.html


如有好文章投稿,請點擊 → 這裡了解詳情




字典對象在Python中作為最常用的數據結構之一,和數字、字元串、列表、元組並列為5大基本數據結構,字典中的元素通過鍵來存取,而非像列表一樣通過偏移存取。筆者總結了字典的一些常用Pyhonic用法,這是字典的Pythonic用法的上篇



0. 使用 in/not in 檢查 key 是否存在於字典




判斷某個 key 是否存在於字典中時,一般初學者想到的方法是,先以列表的形式把字典所有鍵返回,再判斷該key是否存在於鍵列表中:





dictionary

=

{}

keys

=

dictionary

.

keys

()


for

k

in

keys

:


if

key

==

k

:


print

True


break




更具Pythonic的用法是使用in關鍵字來判斷 key 是否 存在於字典中:




if

key

in

dictionary

:

print

True


else

:

print

False




1. 使用 setdefault() 初始化字典鍵值




使用字典的時候經常會遇到這樣一種應用場景:動態更新字典,像如下代碼,如果 key 不在 dictionary 中那麼就添加它並把它對應的值初始為空列表 [] ,然後把元素 append 到空列表中:





dictionary

=

{}


if

"key"

not

in

dictionary

:


dictionary

[

"key"

]

=

[]


dictionary

[

"key"

].

append

(

"list_item"

)




儘管這段代碼沒有任何邏輯錯誤,但是我們可以使用setdefault來實現更Pyhonic的寫法:





dictionary

=

{}


dictionary

.

setdefault

(

"key"

,

[]).

append

(

"list_item"

)




字典調用 setdefault 方法時,首先檢查 key 是否存在,如果存在該方法什麼也不做,如果不存在 setdefault 就會創建該 key,並把第二個參數[]作為 key 對應的值。




2. 使用 defaultdict() 初始化字典




初始化一個字典時,如果初始情況下希望所有 key 對應的值都是某個默認的初始值,比如有一批用戶的信用積分都初始為100,現在想給 a 用戶增加10分





d

=

{}


if

"a"

not

in

d

:


d

[

"a"

]

=

100


d

[

"a"

]

+=

10




同樣這段代碼也沒任何問題,換成更pyhtonic的寫法是:





from

collections

import

defaultdict


d

=

defaultdict

(

lambda

:

100

)


d

[

"a"

]

+=

10




defaultdict 是位於 collections 模塊下,它是 dict 類的子類,語法結構是:





class collections.defaultdict([default_factory[, ...]])




第一個參數default_factory是一個工廠方法,每次初始化某個鍵的時候將會被調用,value就是default_factory返回的值,剩下的參數和dict()函數接收的參數一樣




3. 使用 iteritems() 迭代大數據




迭代大數據字典時,如果是使用 items() 方法,那麼在迭代之前,迭代器迭代前需要把數據完整地載入到內存,這種方式不僅處理非常慢而且浪費內存,下面代碼約佔1.6G內存(為什麼是1.6G?可以參考:




http://stackoverflow.com/questions/4279358/pythons-underlying-hash-data-structure-for-dictionaries)





d

=

{

i

:

i

*

2

for

i

in

xrange

(

10000000

)}


for

key

,

value

in

d

.

items

()

:


print

(

"{0} = {1}"

.

format

(

key

,

value

))




而使用 iteritem() 方法替換 items() ,最終實現的效果一樣,但是消耗的內存降低50%,為什麼差距那麼大呢?因為 items() 返回的是一個 list,list 在迭代的時候會預先把所有的元素載入到內存,而 iteritem() 返回的一個迭代器(iterators),迭代器在迭代的時候,迭代元素逐個的生成。





d

=

{

i

:

i

*

2

for

i

in

xrange

(

10000000

)}


for

key

,

value

in

d

.

iteritem

()

:


print

(

"{0} = {1}"

.

format

(

key

,

value

))




4. 高效合并字典




普通方法




合并多個字典的時候可以用一行代碼實現:





x

=

{

"a"

:

1

,

"b"

:

2

}


y

=

{

"b"

:

3

,

"c"

:

4

}


z

=

dict

(

x

.

items

()

+

y

.

items

())




這種寫法看起來很Pythonic,但仔細分析的話,它的執行效率並不高,items()方法在python2.7中返回的是列表對象,兩個列表相加得到一個新的列表,這樣內存中存在3個列表對象,如果兩個列表的大小都是1G,那麼執行這段代碼需要佔用4G的空間來創建這個字典。此外這段代碼在Python3中會報錯,因為python3中items()返回的是dict_items對象,而不是列表。





>>>

c

=

dict

(

a

.

items

()

+

b

.

items

())


Traceback

(

most recent call

last

)

:


File

""

,

line

1

,

in

<

module

>


TypeError

:

unsupported operand type

(

s

)

for

+:

"dict_items"

and

"dict_items"




在python3中,你需要明確地強制轉換成list對象:





z = dict(list(x.items()) + list(y.items()))




Pythonic方法




在Python3.5中提供了一種全新的Pythonic方法:





z = {**x, **y}




不過考慮到大部分系統還是基於Python2,所以一種更兼容的pythonic方法是:





z

=

x

.

copy

()


z

.

update

(

y

)




當然,你可以把它封裝成一個函數:





def

merge_dicts

(

*

dict_args

)

:


"""


可以接收1個或多個字典參數


"""


result

=

{}


for

dictionary

in

dict_args

:


result

.

update

(

dictionary

)


return

result



z

=

merge_dicts

(

a

,

b

,

c

,

d

,

e

,

f

,

g

)




其他方法




還有其他方式來合并字典,但是性能不一定是最優的,比如: python2.7可以支持字典推導式





{k: v for d in dicts for k, v in d.items()}




python2.6及以下版本使用





{k: v for d in dicts for k, v in d.items()}




性能對比





import

timeit


>>>

min

(

timeit

.

repeat

(

lambda

:

{

**

x

,

**

y

}))

# python3.5


0.4094954460160807


>>>

min

(

timeit

.

repeat

(

lambda

:

merge_two_dicts

(

x

,

y

)))


0.5726828575134277


>>>

min

(

timeit

.

repeat

(

lambda

:

{

k

:

v

for

d

in

(

x

,

y

)

for

k

,

v

in

d

.

items

()}

))


1.163769006729126


>>>

min

(

timeit

.

repeat

(

lambda

:

dict

((

k

,

v

)

for

d

in

(

x

,

y

)

for

k

,

v

in

d

.

items

())))


2.2345519065856934




直接使用python3.5中的{**x, **y}是最快的,使用update次之,用字典推導式相對來說是最慢的。




看完本文有收穫?請轉

發分享給更多人


關注「P

ython開發者」,提升Python技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python 的精彩文章:

Python編寫知乎爬蟲實踐
基於 Python 和 Scikit-Learn 的機器學習介紹
Fedora寫給Python的情書
字典對象的 Pythonic 用法
Python將是人工智慧時代的最佳編程語言

TAG:Python |

您可能感興趣

讀懂Python的Mock對象庫(2)
讀懂Python的Mock對象庫(1)
簡單定義Python和Scala的類和對象
Web Pages WebSecurity 對象
POJO 對象setter 方法是否合適return 「this」
XML DOM-ProcessingInstruction 對象
使用Tensorflow Object Detection API實現對象檢測
code值獲取內部枚舉類對象(自定義EnumUtil PackageUtil工具類)
ASP FileSystemObject 對象
Supreme 的下一個奢華品牌合作對象是 Dolce&Gabanna?
Kotlin 類和對象
Web Pages Database 對象
ASP Application 對象
XML DOM-DocumentImplementation 對象
A$AP Rocky 新專輯曲目曝光?合作對象包含 Snoop Dogg、Kid Cudi、T.I. 等人
ASP.NET Web Forms ArrayList 對象
django框架:HttpResponse對象
不打開文件獲取名稱。Excel VBA Application對象GetOpenFilename方法教程
ASP Response 對象
ES6 Promise 對象