python在字典中插入新的資料_python新知識 字典檢視
技術標籤:python在字典中插入新的資料在定時器中返回給檢視的值
python新知識字典檢視
從bug中學習:字典返回的居然是檢視
bug
與之前相同,我在試圖從dataframe
和series
中提取值。
samples = ['R1','R2','R3','R4']df = pd.DataFrame([[True,False,False,True]],columns=samples)series = pd.Series({'R1':True,'R2':False,'R3':False,'R4':True})print(df[samples])print(series[samples])
out:
R1 R2 R3 R40 True False False TrueR1 TrueR2 FalseR3 FalseR4 Truedtype:bool
正常。
但是呢,我在實際程式碼中用到的samples
是一個字典。
samples = {'R1':'R1','R2':'R2','R3':'R3','R4':'R4'} #鍵值相等,僅供演示print(df[samples.keys()])print(series[samples.keys()])
out:
R1 R2 R3 R40 True False False TrueR1 TrueR2 FalseR3 FalseR4 Truedtype: bool
還正常,別急,bug來了。
print(df[samples_map.values()])print(series[samples_map.values()])
out:
R1 R2 R3 R40 True False False TrueKeyError Traceback (most recent call last)-44-c2d1464b8ebd> 1 print(df[samples.values()])----> 2 print(series[samples.values()])KeyError: dict_values(['R1', 'R2', 'R3', 'R4'])
錯誤,而且是取值的鍵錯誤,鍵值是dict_values(['R1', 'R2', 'R3', 'R4'])
,what?
這時候可能就要問了,這突然出現的是dict_values
是什麼鬼東西。為什麼取keys
正常,取values
就錯誤呢?
把keys
和values
都輸出看看。
print(samples.keys())print(samples.values())
out:
dict_keys(['R1', 'R2', 'R3', 'R4'])dict_values(['R1', 'R2', 'R3', 'R4'])
啊,返回的是dict_keys
和dict_values
類的物件,不是列表。
字典檢視
那我們就來研究一下這兩個類。
查閱stackoverflow
[1]和python官方文件[2]後得知,dict.keys()
,dict.values()
和dict.items()
返回的都是檢視物件。他們能夠提供字典記錄的動態檢視,當字典變化時,檢視也會變化。檢視可以通過遍歷產生相應的資料,並支援成員測試。
檢視支援如下操作:
len(dictview)
iter(dictview)
x in dictview
reversed(dictview)
for loop
dict.keys()
返回的鍵檢視是類似集合的,因為鍵記錄的值是唯一併且可hash
的。如果所有的值都可hash
,那麼鍵值對也是唯一且可hash
的,dict.items()
鍵值對檢視也是類似集合的。dict.values()
值檢視通常不被作為類似集合的物件處理,因為值記錄通常不唯一。
對於類似集合的檢視,所有抽象基礎類collections.abc.Set
定義的操作符都是可以進行的,比如==
,<
或者^
。
來一個直觀的例子。
dishes = { 'sausage': 1, 'bacon': 1, 'spam': 500}keys = dishes.keys() # 取出keysvalues = dishes.values() # 取出valuesdel dishes['spam']dishes['eggs'] = 2print(list(keys))print(list(values))
out:
['sausage', 'bacon', 'eggs'][1, 1, 2]
仔細看一下,我明明事先取出了keys
和values
,然後刪除字典的一個鍵值對和新增鍵值對後,取出的keys
和values
居然也跟隨著發生了動態變化。希望你能從這個例子中更好的理解什麼叫檢視。
動態變化會避免以下錯誤發生。
dishes = { 'sausage': 1, 'bacon': 1, 'spam': 500}keys = list(dishes.keys()) #使用list模擬python2del dishes['spam']dishes['eggs'] = 2for i in keys: print(i,dishes[i])
out:
sausage 1bacon 1---------------------------------------------------------------------------KeyError Traceback (most recent call last)-127-e0e39cb366d4> 4 dishes['eggs'] = 2 5 for i in keys:----> 6 print(i,dishes[i])KeyError: 'spam'
不加list
時。
dishes = { 'sausage': 1, 'bacon': 1, 'spam': 500}keys = dishes.keys() del dishes['spam']dishes['eggs'] = 2for i in keys: print(i,dishes[i])
out:
sausage 1bacon 1eggs 2
長度、成員測試、迭代器、集合運算的例子。
print('eggs' in keys)print(len(keys))print(next(iter(keys))) #iter使用可迭代物件生成迭代器,next進行迭代器取值print(keys & {'eggs', 'bacon', 'salad'}) #交集print(keys | {'sausage', 'juice'}) #並集
out:
True3sausage{'eggs', 'bacon'}{'bacon', 'eggs', 'juice', 'sausage'}
檢視的優點在於能夠直接計算長度、進行成員測試且跟隨字典動態變化。
思考
那檢視和之前的bug
有什麼關係呢。從官方文件可以看出,keys
檢視和values
檢視的唯一區別在於,keys
裡的值可hash
,而values
裡的值預設不可hash
。
但是更奇怪的事發生了。
hash(samples.keys())
out:
---------------------------------------------------------------------------TypeError Traceback (most recent call last)input----> 1 hash(samples.keys())TypeError:unhashabletype:'dict_keys'
hash(samples.values())
out:
-9223371939388789928
values
檢視物件整體居然可hash
,而且官方說d.values() == d.values()
恆為False
。啊這,不會是因為hash
值可變吧,再來一次。
hash(samples.values())
out:
-9223371939388789799
果然每次值都不一樣。
不過,真的不知道values
檢視物件可hash
是為了什麼,反倒是如果keys
檢視物件可hash
我倒是能理解。
與此同時,pandas
官方文件[3]中說。
An Index instance canonlycontain hashable objects
index
只能包含可hash
物件,而且之前的錯誤詳細資訊裡也有部分提到了hashable
(之前省略了)。
pandas\_libs\index.pyx in pandas._libs.index.IndexEngine.get_value()pandas\_libs\index.pyx in pandas._libs.index.IndexEngine.get_value()pandas\_libs\index.pyx in pandas._libs.index.IndexEngine.get_loc()pandas\_libs\hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()pandas\_libs\hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()KeyError: dict_values(['R1', 'R2', 'R3', 'R4'])
所以有可能是因為values
檢視物件可hash
導致series
認為該返回值是一個單獨的鍵,而不是多個鍵的集合,於是series
直接提取該鍵的對應值,從而產生錯誤。
fix bug
解決起來當然很簡單了。
print(series[list(samples.values())])print(series[iter(samples.values())])
out:
R1 TrueR2 FalseR3 FalseR4 Truedtype: boolR1 TrueR2 FalseR3 FalseR4 Truedtype:bool
但是我又發現,使用.loc
也可以解決該bug。
print(series.loc[samples.values()])
out:
R1 TrueR2 FalseR3 FalseR4 Truedtype: bool
.loc
就可以識別出該返回值包含多值並逐個取值,但是直接[]
卻不行。難道是因為.loc
優先檢驗提供的值是否可迭代?
本文涉及內容過於複雜,結論僅為猜想。歡迎提出意見。
我
我是SSSimon Yang,關注我,用code解讀世界
References
[1]
stackoverflow
:https://stackoverflow.com/questions/8957750/what-are-dictionary-view-objects[2]
python官方文件:https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects[3]
pandas
官方文件:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.html