《Fluent Python》学习笔记:第 3 章 字典和集合

3.1 泛映射类型

collections.abc模块中有MappingMutableMapping这两个抽象基类,它们的作用是为字典和集合dict和其他类似的类型定义形式接口。

from collections import abc
my_dict = {}
print(isinstance(my_dict, abc.Mapping))  # isinstance 可以用来判定某个数据是不是广义上的映射类型

标准库里的所有映射类型都是利用dict来实现的,因此只有可散列的数据类型才能用作这些映射里的键(值无此限制)。

可散列的数据类型

基本特征:

  • 该对象的生命周期中,散列值不变
  • 实现了__hash__()方法、__qe__()方法, 这样才能跟其他键做比较
  • 如果两个可散列对象是相等的,那么它们的散列值一定是一样的
  • 一般来讲用户自定义的类型的对象都是可散列的,散列值就是它们的id() 函数的返回值,所以所有这些对象在比较的时候都是不相等的。

可散列的数据类型:

  • 原子不可变数据类型(str、bytes 和数值类型),frozenset (只能容纳可散列类型)
  • 所有元素都是可散列类型的元组
tt = (1, 2, (30, 40))
print(hash(tt))     # -3907003130834322577
tl = (1, 2, [30, 40])
print(hash(tl))     # TypeError: unhashable type: 'list'
tf = (1, 2, frozenset([30, 40]))
print(hash(tf))     # 5149391500123939311

字典的不同创建方式

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({'three': 3, 'one': 1, 'two': 2})
print(a == b == c == d == e)   # True

3.2 字典推导

# 示例 1 字典推导的应用
DIAL_CODES = [(86, 'China'),(91, 'India'),(1, 'United States'),(62, 'Indonesia')]
country_code = {country: code for code, country in DIAL_CODES}
print(country_code)  # {'China': 86, 'India': 91, 'United States': 1, 'Indonesia': 62}
code_country = {code: country.upper() for country, code in country_code.items() if code < 66}
print(code_country)  # {1: 'UNITED STATES', 62: 'INDONESIA'}

3.3 常见的映射方法

方法列表

update 鸭子类型

update 方法处理参数m 的方式,是典型的“鸭子类型”。

  • 函数首先检查m是否有keys 方法,如果有,那么update 函数就把它当作映射对象来处理。否则,函数会退一步,转而把m 当作包含了键值对(key, value) 元素的迭代器。
  • Python 里大多数映射类型的构造方法都采用了类似的逻辑,因此你既可以用一个映射对象来新建一个映射对象,也可以用包含(key, value) 元素的可迭代对象来初始化一个映射对象。
tinydict = {'Name': 'Runoob', 'Age': 7}
tinydict2 = {'Sex': 'female', 'Age': 9}

tinydict.update(tinydict2)
print(tinydict)   # {'Name': 'Runoob', 'Age': 9, 'Sex': 'female'}

用setdefault处理找不到的键

  • d.get(k, default)
    可用来代替d[k],给找不到的键一个默认的返回值。
  • d.setdefault(key, []).append(new_value)
    当key不存在时,新增该键值对。相比for循环,减少查询次数。
student_a = {'Name': 'Runoob', 'Age': 9, 'Sex': 'female'}
student_a.setdefault("Age", 16)   # 如果key存在,则不修改值
student_a.setdefault("school", "NJU")
student_a.setdefault("girlfriend", []).append("Ann")
print(student_a)   # {'Name': 'Runoob', 'Age': 9, 'Sex': 'female', 'school': 'NJU', 'girlfriend': ['Ann']}
# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和下标,多用于for循环中。
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print(list(enumerate(seasons)))      # [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
print(list(enumerate(seasons, start=1)))       # [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

3.4 映射的弹性键查询

3.4.1 defaultdict:处理找不到的键的一个选择

上述的setdefault方法在每次调用时都要我们手动指定默认值,那有没有什么办法能方便一些,在键不存在时,直接返回我们指定的默认值?两个常用的方法是:①使用defaultdict类;②自定义一个dict子类,在子类中实现__missing__方法,而这个方法又有至少两种方法。

collections.defaultdict( [default_factory[, …] ])

该函数返回一个类似字典的对象。defaultdict是Python内建字典类(dict)的一个子类,它重写了方法_missing_(key),增加了一个可写的实例变量default_factory,实例变量default_factorymissing()方法使用,如果该变量存在,则用以初始化构造器,如果没有,则为None。其它的功能和dict一样。

import collections
my_dict = collections.defaultdict(list)
my_dict[key].append(new_value)  # 无需判断键key是否存在于my_dict中

在实例化defaultdict时,需要给构造方法提供一个可调用对象(实现了__call__方法的对象),这个可调用对象存储在defaultdict类的属性default_factory中,当__getitem__找不到所需的键时就会通过default_factory来调用这个可调用对象来创建默认值。

上述代码中my_dict[key]的内部过程如下(假设key是新键):

  • 调用list()来创建一个新列表;
  • 把这个新列表作为值,key作为它的键,放到my_dict中;
  • 返回这个列表的引用。

如果在实例化defaultdict时未指定default_factory,那么在查询不存在的键时则会触发KeyErrordefaultdict中的default_factory只会在__getitem__里被调用,在其它的方法里完全不会发挥作用!比如,dd是个defaultdict,k是个不存在的键,dd[k]这个表达式则会调用default_factory,并返回默认值,而dd.get(k)则会返回None。

3.4.2 特殊方法__missing__

  • __getitem__找不到键的时候,Python 就会自动调用__missing__,而不是抛出一个KeyError 异常。(__missing__方法只会被__getitem__调用,比如在表达式d[k] 中)。
  • __getitem__调用__missing____missing__调用default_factory
# StrKeyDict0 在查询的时候把非字符串的键转换为字符串
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:
            return self[key]
        except KeyError:
            return default

    def __contains__(self, key):   # k in d 会调用__contains__,但从dict 继承的__contains__ 在找不到键时不会调用__missing__方法
        return key in self.keys() or str(key) in self.keys()
    ```
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值