您的位置:首页 > 编程语言 > Python开发

python 字典详解 二(字典拓展使用 )

2016-03-11 12:00 639 查看

前言

本文将涵盖在一些特殊用途的字典,如OrderedDict, defaultdict, ChainMap, Counter,它们都在 collections 类库下面,另外还有

types下面的MappingProxyType

weakref 下面的 WeakKeyDictionary, WeakValueDictionary

OrderedDict

它主要的作用是字典排序, 标准的字典插入到字典中的键值对是没有顺序的,但是在OrderedDict中,会按照插入的顺序排序字典

from collections import OrderedDict
order_dict = OrderedDict()


#排序方式为按插入的先后顺序
order_dict['one'] = 1
order_dict[1] = 'one'
order_dict['two'] = 2
order_dict


OrderedDict([(‘one’, 1), (1, ‘one’), (‘two’, 2)])

上面生成的是一个OrderedDict对象,由于它是dict的子类,它具有dict的全部功能,这里可以进行直接转换

dict(order_dict)


{‘two’: 2, 1: ‘one’, ‘one’: 1}

这样一来, 由于按先后顺序排序,因此可以通过排序方法改变插入键值对的顺序,进而使得字典按我们要的方式排序

test_dict = dict([('one', 1), (1, 'one'), ('two', 2)])
# 以值的长度从小到大排序
OrderedDict(sorted(test_dict.items(), key=lambda t: len(str(t[1]))))


OrderedDict([(‘one’, 1), (‘two’, 2), (1, ‘one’)])

另外由于插入顺序的原因, 还可以使它成为一个队列(FIFO)或者堆栈的结构(LIFO)

order_dict['fifo'] = 'test'
order_dict


OrderedDict([(‘one’, 1), (1, ‘one’), (‘two’, 2), (‘fifo’, ‘test’)])

# 先进先出
order_dict.popitem(last=False)
order_dict


OrderedDict([(1, ‘one’), (‘two’, 2), (‘fifo’, ‘test’)])

#后进先出
order_dict['lifo'] = 'test'
order_dict.popitem(last=True)
order_dict


OrderedDict([(1, ‘one’), (‘two’, 2), (‘fifo’, ‘test’)])

在3.2版本中增加了move_to_end(key, last=True)方法,可以将某个key进行前后移动

order_dict.move_to_end(1, last=True)
order_dict


OrderedDict([(‘two’, 2), (‘fifo’, ‘test’), (1, ‘one’)])

注: 在3.5中还增加了 reverse反转方法,可以将OrderedDict倒过来

defaultdict

defaultdict 继承了dict,但增加魔法方法
__missing__(key)
,在默认
key
没有找到的情况会进入这个方法 ,在这个方法中,当
default_factory
参数不为
none
时, 它会将
default_factory
以没有参数的形式调用,并添加到这个
key
上面,同时返回这个值,这个
defaultdict
带来的好处就是,可以把没有在字典中的
key
的值,直接拿来使用.

比如给一个不存在的key赋值为一个字典,并往字典里添加一个键值对

from collections import defaultdict


dd = defaultdict(dict)


dd['missing_key']['a'] = 1


dd['missing_key']


{'a': 1}


可以构造自己的default_factory

def factory_method():
abc = {'a': 1}return abc


dd_0 = defaultdict(factory_method)


dd_0['missing_key']['b'] = 2


dd_0['missing_key']


{'a': 1, 'b': 2}


它经常会使用到到的一场景,是给字典中不存在的键,赋值一个空的列表,然后再往列表里面插入东西, 这种方式比用
setdefault
方法效率更高,更简洁

dl = defaultdict(list)
dl['missing_key'].append('something)


ChainMap

它非常适合来给一堆映射关系做操作,按照某种顺序和一定逻辑来处理这些映射关系,或查找,或更新删除

比如最直接点的例子就是,搜索python中某个程序的变量或者方法时,因为它可能在
locals
globals
builtins
中,如果找到了它就不继续往下搜索了, 所以可以将它们按顺序链起来,然后依次查找,找到即返回

from collections import ChainMap
import builtins
pylookup = ChainMap(locals(), globals(), vars(builtins))


下面举一个实际操作的例子,有三个字典,将它们链起来

dict1 = {'a': 'a1'}
dict2 = {'a': 'a2'}
dict3 = {'b': 'b3'}


dict_chain =  ChainMap(dict1, dict2, dict3)


查找这个链上面的 键 ‘a’ 对应的值, 直接找到
dict1
就返回了

dict_chain['a']


‘a1’

b
也是按从左到右的顺序查找的

dict_chain['b']


‘b3’

如果添加一个键值对,在链上没有的,默认会插入到链里第一个字典中

dict_chain['c'] = 'c1'
dict1


{‘a’: ‘a1’, ‘c’: ‘c1’}

在这个链上,可以通过
maps
方法,来指定某个字典,比如往链上第二个字典中插入键值对

dict_chain.maps[1]['d'] = 'd2'
dict2


{‘a’: ‘a2’, ‘d’: ‘d2’}

如果不想更改这个链上的值,但又希望能够找到,可以使用
new_child
方法,它会添加一个新的节点到这个链上的第一个位置,所有的删除和修改操作只影响这个新添加的节点,因为
new_child
方法返回的链,是把原来的链视为父链,并规定了只能查看不能修改父链.

dict4 = dict_chain.new_child()


dict_chain


ChainMap({‘c’: ‘c1’, ‘a’: ‘a1’}, {‘d’: ‘d2’, ‘a’: ‘a2’}, {‘b’: ‘b3’})

dict4.parents


ChainMap({‘c’: ‘c1’, ‘a’: ‘a1’}, {‘d’: ‘d2’, ‘a’: ‘a2’}, {‘b’: ‘b3’})

dict4['e'] = 'e4'


dict4


ChainMap({'e': 'e4'}, {'c': 'c1', 'a': 'a1'}, {'d': 'd2', 'a': 'a2'}, {'b': 'b3'})


即使父链中有这个键,也只影响子节点

dict4['a'] = 'test'
dict4


ChainMap({'a': 'test', 'e': 'e4'}, {'c': 'c1', 'a': 'a1'}, {'d': 'd2', 'a': 'a2'}, {'b': 'b3'})


可以访问父链

dict4['b']


'b3'


删除父链中键,如果新增节点中没有,抛出KeyError异常

del dict4['b']


---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

C:\Python34\lib\collections\__init__.py in __delitem__(self, key)
865         try:
--> 866             del self.maps[0][key]
867         except KeyError:

KeyError: 'b'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)

<ipython-input-38-76b7cb5f9ccf> in <module>()
----> 1 del dict4['b']

C:\Python34\lib\collections\__init__.py in __delitem__(self, key)
866             del self.maps[0][key]
867         except KeyError:
--> 868             raise KeyError('Key not found in the first mapping: {!r}'.format(key))
869
870     def popitem(self):

KeyError: "Key not found in the first mapping: 'b'"


del dict4['a']
dict4


ChainMap({‘e’: ‘e4’}, {‘c’: ‘c1’, ‘a’: ‘a1’}, {‘d’: ‘d2’, ‘a’: ‘a2’}, {‘b’: ‘b3’})

对于
ChainMap
同样可以使用
items
进行遍历,但是它会输出一种遍历顺序,优先考虑靠链前面的字典的键.

for key, value in dict4.items():
print(key, value)


a a1
b b3

c c1
d d2

e e4

可以通过dict方法直接转换为普通的字典对象

dict(dict4)


{'a': 'a1', 'b': 'b3', 'c': 'c1', 'd': 'd2', 'e': 'e4'}


* 注: 如何定制ChainMap,可以参考 library 文档, 这里边举了一个DeepChainMap的例子 *

Counter

主要用于给重复元素计数的, 3.1版本后新增了很多方法,官方文档中有很多很好的例子,

这里注意以下几点

Counter只接受可迭代对象和映射对象,作为构造参数

因此字符串也可以直接为Counter构造参数

from collections import Counter


c = Counter('abbbsdsdf')


c


Counter({‘a’: 1, ‘b’: 3, ‘d’: 2, ‘f’: 1, ‘s’: 2})

在Counter中,值都是数值类型,因此它可以是负数和0

如果键不在Counter中,默认计数为0

c['w']


0

Counter可以像集合那样使用,不过它是多重集合

c0 = Counter({'w': 2})
c | c0


Counter({‘a’: 1, ‘b’: 3, ‘d’: 2, ‘f’: 1, ‘s’: 2, ‘w’: 2})

MappingProxyType

毫无疑问它是一个mapping对象, 但是它是不可修改的, 比如在方法里面传一个字典对象,不希望在方法里面修改这个字典, 只允许访问操作,就可以用到它了。

from types import MappingProxyType
test_dict = {'a': 1}


dict_proxy = MappingProxyType(test_dict)


dict(dict_proxy)


{'a': 1}


不管是更新还是增加键值对都会抛出异常

dict_proxy.update({'b': 2})


---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-6-6cf6b228d778> in <module>()
----> 1 dict_proxy.update({'b': 2})

AttributeError: 'mappingproxy' object has no attribute 'update'


dict_proxy['b'] = 2


---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-7-0f8414eed34e> in <module>()
----> 1 dict_proxy['b'] = 2

TypeError: 'mappingproxy' object does not support item assignment


dict_proxy['a'] = 'a'


---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-9-f5e28375c561> in <module>()
----> 1 dict_proxy['a'] = 'a'

TypeError: 'mappingproxy' object does not support item assignment


访问键值对就没有问题

dict_proxy['a']


1


WeakValueDictionary, WeakKeyDictionary

弱引用mapping对象, 以WeakValueDictionary来说, 当键对应的值除了mapping本身引用之外没有其他引用的情况下,会回收掉这个值,相应的这个键值对就不存在了

import weakref

class TestObject(object):
value = 'hello weakref'

d = weakref.WeakValueDictionary()
d[1] = TestObject()


除了 d[1] 引用这个对象之外没有其他引用,立即被垃圾回收器收回了

d[1]


---------------------------------------------------------------------------

KeyError Traceback (most recent call last)

<ipython-input-19-6ee4d8336f71> in <module>()
----> 1 d[1]

C:\Python34\lib\weakref.py in __getitem__(self, key)
123
124 def __getitem__(self, key):
--> 125 o = self.data[key]()
126 if o is None:
127 raise KeyError(key)

KeyError: 1


d


<WeakValueDictionary at 76992912>


obj = TestObject()


d[1] = obj


由于obj还被main模块所引用,因此可以访问到这个键值对

d[1]


<__main__.TestObject at 0x944650>


注: 对WeakValueDictionary对象使用valuerefs()方法进行遍历时,并不能保证这个字典不会改变,可能存在遍历过程中值被垃圾回收器收回的风险。

对于WeakKeyDictionary来说它类似于WeakValueDictionary, 只不过这时候弱引用作用于键上面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: