流程的Python——字典

1.在Python中标准库中所有映射类型都是利用dict来实现的,因此它们有个共同的限制,即只有可散列 的数据类型才能用作这些映射的键,注意只有键有这个要求,值并不需要是可散列的数据类型。


在Python中原子不可变数据类型(str、bytes和数值类型)都是可散列类型,frozenset也是可散列类型,因为根据其定义,frozenset里只能容纳可散列类型。元组的话,只有当一个元组里包含的所有元素都是可散列类型的情况下,它才是可散列的。


一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们的id()函数的返回值。


2.字典的创建形式有如下几种方法:

a = dict(one=1, two=2, three=3)
b = {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'one': 1, 'two': 2, 'three': 3})
print(a==b==c==d==e)

当然还可以利用字典推导来创建新的字典

DIAL_CODES = [
    (86, 'China'),
    (91, "India"),
    (1, "United States"),
    (62, 'Indonesia'),
    (55, 'Brazil'),
    (92, 'Pakistan'),
    (880, "Bangladesh"),
    (234, "Nigeria"),
    (7, 'Russia'),
    (81, 'Japan'),
]

country_code = {country: code for code, country in DIAL_CODES} 
print(country_code)
code_country = {code: country.upper() for country, code in country_code.items() if code < 66}
print(code_country)

 

3.常见的映射方法

 

dict

defaultdict

OrederedDict

 

d.clear()

移除所有元素

d.__contains__(k)

检查k是否在d中(k是键)

d.copy()

浅复制

d.__copy__()

 

 

用于支持copy.copy

d.default_factory

 

 

在 __missing__ 函数中被调用的函数,用以给未找到的元素这只值

d.__delitem__(k)

del d[k] 移除键为k的元素

d.fromkeys(it, [initial])

将迭代器it里的元素设置为映射里的键,如果有initial参数,就把它作为这些键对应的值。(默认是None)

d.get(k, [default])

返回键k对应的值,如果字典里没有键k,则返回None或者default

d.__getitem__(k)

让字典d能用d[k] 的形式返回键k对应的值

d.items()

返回d里所有的键值对

d.__iter__()

获取键的迭代器

d.keys()

获取所有键

d.__len__()

可以用len(d) 的形式得到字典里键值对的数量

d.__missing__(k)

 

 

当 __getitem__ 找不到对应键的时候,这个方法会被调用

d.move_to_end(k, [last])

 

 

把键为k的元素移动到最靠前或者最靠后的位置(last的默认值是True)

d.pop(k, [default])

返回键k所对应的的值,然后移除这个键值对。如果没有这个键,返回None或者default

d.popitem()

随机返回一个键值对并从字典里移除它

d.__reversed__()

 

 

返回倒叙的键的迭代器

d.setdefault(k, [default])

若字典里有键k,则直接返回k所对应的值;若无,则让d[k]=default,然后返回default

d.__setitem__(k, v)

实现d[k]=v操作,把k对应的值设为v

d.update(m, [**kargs])

m可以是映射或者键值对迭代器,用来更新d里对应的条目

d.values()

返回字典里的所有值

用setdefault处理找不到的键,示例如下:

"""创建一个从单词到其出现情况的映射"""
import sys
import re

WORD_RE = re.compile(r'\w+')

index = {}
with open("xx.txt",  encoding='UTF-8') as fp:
    # enumerate(sequence, [start=0]) sequence -- 一个序列、迭代器或其他支持迭代对象。start -- 下标起始位置。
    for line_no, line in enumerate(fp, 1):
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            # 下面是一种不好的实现
            occurrences = index.get(word, [])
            occurrences.append(location)
            index[word] = occurrences
            # 上面三行等价于下面一行
            # 获取单词的出现情况列表,如果单词不存在,把单词和一个空列表放进映射,然后返回这个空列表
            # 这样就能在不进行第二次查找的情况下更新列表
            index.setdefault(word, []).append(location)
# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
    print(word, index[word])

在上述的两种实现中,二者的效果都是一样的,只不过前者至少要进行两侧键查询-如果键不存在则是三次,用setdefault只需要一次就可以完成整个操作。

 

4.映射的弹性键查询

有时候为了方便起见,就算某个键在映射里不存在,我们也希望在通过这个键读取值的时候能得到一个默认值。有两种方法可以帮我们达到这个目的,一个是通过defaultdict这个类型而不是普通的dict, 另一个是自己定义一个dict的子类,然后在子类中实现__missing__方法。

 方法一如下:

import sys
import re
import collections

WORD_RE = re.compile(r'\w+')

# 把list构造方法作为default_factory来创建一个defaultdict
index = collections.defaultdict(list) 
with open(sys.argv[1], encoding='UTF-8') as fp:
    # enumerate(sequence, [start=0]) sequence -- 一个序列、迭代器或其他支持迭代对象。start -- 下标起始位置。
    for line_no, line in enumerate(fp, 1):
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            # 如果index并没有word的记录,那么default_factory会被调用,为查询不到的键创造一个值。
            # 这个值在这里是一个空列表,然后这个空列表会被赋值给index[word],继而被当做返回值返回
            # 因此,append(location)操作总能成功
            index[word].append(location)

# 以字母顺序打印出结果
for word in sorted(index, key=str.upper):
    print(word, index[word])

在创建defaultdict对象的时候,我们需要给它配置一个为找不到键创建默认值的方法。具体而言,在实例化一个defaultdict的时候,需要给构造方法提供一个可调用对象,这个可调用对象会在__getitem__碰到找不到的键的时候被调用,让 __getitem__返回一个默认值。

比如在上面,我们在实例化defaultdict的时候,提供了list可调用对象,当 __getitem__找不到键的时候,会按一下步骤来走:

1.调用list()来创建一个新列表

2.把这个新列表作为值,找不到的那个键作为它的键,放入字典中

3.返回这个列表的引用

这个用来生成默认值的可调用对象存放在名为default_factory的实例属性中。如果在创建defaultdict的时候没有指定default_factory,查询不存在的键会触发KeyError。

方法二(__missing__)如下:


class StrKeyDict0(dict):

    def __missing__(self, key):
        if isinstance(key, str):    # 如果找不到的键本身就是字符串,那就抛出异常
            raise KeyError(key)
        return self[str(key)]   # 如果找不到的键是字符串,那么把它转换成字符串再进行查找

        def get(self, key, default=None):
            try:
                # get方法把查找工作用self[key]的形式委托给__getitem__,这样在宣布查找失败之前
                # 还能通过 __missing__ 再给某个键一个机会
                return self[key]
            except KeyError:
                return default

        def __contains__(self, key):
            return key in self.keys() or str(key) in self.keys()

if __name__ == "__main__":
    d = StrKeyDict0([('2', 'two'), ('4', 'four')])
    print(d[2])
    print(d['2'])
    print(d.get('2'))
    print(d.get(2))
    print(d.get(1, '不存在'))

注意:__missing__方法只会被__getitem__调用(比如在表达式d[k]中)。像 k in dict.keys() 这种操作在Python3中是很快的,而且即便映射类型对象很庞大也没关系,因为dict.keys()返回的值是一个“视图”。视图就像一个集合,而且跟字典类似的是,在视图里查找一个元素的速度很快。

 

Collections.Counter 一个很有用的映射类型,这个类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列对象技术,或者是当做多重集来用——对重集合就是集合里的元素可以出现不止一次。

ct = collections.Counter(["Hello", "sss", "sss", "sss", "hello"])
print(ct)

 

5.不可变映射类型

从Python3.3开始,types模块引入了一个封装类名叫MappingProxyType。如果给这个类一个映射,它会返回一个只读的映射视图。虽然是个只读的视图,但是它是动态的,即如果原映射出现了改动,那么我们可以通过这个视图观察到,但是无法通过这个视图对原映射进行修改。

from types import MappingProxyType

d = {1: 'A'}
d_proxy = MappingProxyType(d)
print(d_proxy)  # d中的内容可以通过d_proxy看到
# d_proxy[1] = 'X'  # 但是通过d_proxy并不能做任何修改
print(d_proxy)
d[1] = 'X'
print(d_proxy)  # d_proxy是动态的,即对d所做的任何修改都会反馈到它上面

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章