python在字典中插入新的数据_python新知识 字典视图

python新知识   字典视图

从bug中学习:字典返回的居然是视图

bug

与之前相同,我在试图从dataframeseries中提取值。

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就错误呢?

keysvalues都输出看看。

print(samples.keys())print(samples.values())

out:

dict_keys(['R1', 'R2', 'R3', 'R4'])dict_values(['R1', 'R2', 'R3', 'R4'])

啊,返回的是dict_keysdict_values类的对象,不是列表。

字典视图

那我们就来研究一下这两个类。

查阅stackoverflow[1]和python官方文档[2]后得知,dict.keys()dict.values()dict.items()返回的都是视图对象。他们能够提供字典记录的动态视图,当字典变化时,视图也会变化。视图可以通过遍历产生相应的数据,并支持成员测试。

视图支持如下操作:

len(dictview)iter(dictview)x in dictviewreversed(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]

仔细看一下,我明明事先取出了keysvalues,然后删除字典的一个键值对和添加键值对后,取出的keysvalues居然也跟随着发生了动态变化。希望你能从这个例子中更好的理解什么叫视图。

动态变化会避免以下错误发生。

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: unhashable type: '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 can only contain 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解读世界

3a055b55aecade2b031bb9a8d2fb052b.png

References

[1] stackoverflowhttps://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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值