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

前言

本文将涵盖在一些特殊用途的字典,如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中某个程序的变量或者方法时,因为它可能在 localsglobalsbuiltins中,如果找到了它就不继续往下搜索了, 所以可以将它们按顺序链起来,然后依次查找,找到即返回

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)

 in ()
----> 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)

 in ()
----> 1 dict_proxy.update({'b': 2})


AttributeError: 'mappingproxy' object has no attribute 'update'
dict_proxy['b'] = 2
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
----> 1 dict_proxy['b'] = 2


TypeError: 'mappingproxy' object does not support item assignment
dict_proxy['a'] = 'a'
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
----> 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)

 in ()
----> 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

obj = TestObject()
d[1] = obj

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

d[1]
<__main__.TestObject at 0x944650>

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

对于WeakKeyDictionary来说它类似于WeakValueDictionary, 只不过这时候弱引用作用于键上面。

你可能感兴趣的:(python基础,python3,python-lib)