当键不在字典时,该如何处理?

  • 一种方案用 get,为什么是比较好的选择?

一个例子(字典 str-int)

  • 例如,我们要给一家三明治店设计菜单,所以想先确定大家喜欢吃哪些类型的面包。
  • 于是,我们定义这样一个字典,把每种款式的名字和它当前的得票数关联起来。

    1. counters = {
    2. 'pumpernickel': 2,
    3. 'sourdough': 1,
    4. }

    1 判断方式

  • 如果要记录新的一票,首先得判断对应的键在不在字典里。

  • 如果不在,那就把这个键已经得到的票数默认为 0,然后增加得票数。
    • 这需要两次访问这个键
      • 第一次是为了判断它在不在字典里,
      • 第二次是为了用它来获取对应的值,
      • 而且还要做一次赋值。 ```python key = ‘wheat’

if key in counters: count = counters[key] else: count = 0

counters[key] = count + 1

print(counters)

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22011425/1647914852138-f6edafbb-425e-4c16-bcc8-d7b3d2f83a02.png#clientId=ufa0f741f-3cf7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=37&id=ub9b44025&margin=%5Bobject%20Object%5D&name=image.png&originHeight=37&originWidth=458&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5255&status=done&style=none&taskId=u9762ec75-09fe-48c5-9584-d076268cee7&title=&width=458)
  2. <a name="iQSDt"></a>
  3. ## 2 利用 keyError 异常
  4. - 只需要访问一次键名就可以了(也就是在试图获取键值的那行语句中)
  5. ```python
  6. key = 'brioche'
  7. try:
  8. count = counters[key]
  9. except KeyError:
  10. count = 0
  11. counters[key] = count + 1
  12. print(counters)

image.png

3 利用 get 处理

  • 第一个参数指定自己想查的键,
  • 第二个参数指定这个键不存在时应返回的默认值。 ```python key = ‘multigrain’

count = counters.get(key, 0) counters[key] = count + 1

print(counters)

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22011425/1647915207824-dc89ea99-6a26-490d-b29a-8d5dbe8d27dc.png#clientId=ufa0f741f-3cf7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=40&id=u93cb9a2e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=40&originWidth=469&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5391&status=done&style=none&taskId=uce84cfa2-dc2d-446f-b610-b511ae83e56&title=&width=469)
  2. <a name="X7n6D"></a>
  3. # 另外一个例子(字典 str-list)
  4. - 紧接上面的例子,这次不仅要记录每种面包的得票数,而且要记录投票的人。
  5. - 那可以像下面这样,把面包的名称(也就是键名)跟一份列表关联起来,而那份列表指的就是喜欢这种面包的人。
  6. ```python
  7. votes = {
  8. 'baguette': ['Bob', 'Alice'],
  9. 'ciabatta': ['Coco', 'Deb'],
  10. }

1 判断方式

  • 判断字典是否有该键,如果有,提取这个 item 作 append
  • 否则 ```python key = ‘brioche’ who = ‘Elmer’

if key in votes: names = votes[key] else: names = votes[key] = [] #

names.append(who) print(votes)

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22011425/1647917059963-1d14e5a3-a566-4580-8df0-1364ee34aadf.png#clientId=ufa0f741f-3cf7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=37&id=u36ee6154&margin=%5Bobject%20Object%5D&name=image.png&originHeight=37&originWidth=579&originalType=binary&ratio=1&rotation=0&showTitle=false&size=6050&status=done&style=none&taskId=udf82e125-82cf-484b-a462-25915093c59&title=&width=579)
  2. <a name="rALR4"></a>
  3. ## 2 利用 keyError 异常
  4. ```python
  5. key = 'rye'
  6. who = 'Felix'
  7. try:
  8. names = votes[key]
  9. except KeyError:
  10. votes[key] = names = []
  11. names.append(who)
  12. print(votes)

image.png

3 利用 get 处理

  1. key = 'wheat'
  2. who = 'Gertrude'
  3. names = votes.get(key)
  4. if names is None:
  5. votes[key] = names = []
  6. names.append(who)
  7. print(names)

image.png

可以用赋值表达式

  1. key = 'brioche'
  2. who = 'Hugh'
  3. if (names := votes.get(key)) is None: # 赋值
  4. votes[key] = names = []
  5. names.append(who)
  6. print(votes)

4 利用 setdefault 方法

  • 这个方法会查询字典里有没有这个键,
    • 如果有,就返回对应的值;
    • 如果没有,就先把用户提供的默认值跟这个键关联起来并插入字典(get不会)
      • 然后返回这个值。 ```python key = ‘cornbread’ who = ‘Kirk’

names = votes.setdefault(key, []) names.append(who)

print(votes)

  1. - 可能有点难懂,如果这个键本来就包括值,那么就是返回值而已
  2. <a name="XcR1m"></a>
  3. ### 可能产生较大的性能开销
  4. ```python
  5. data = {}
  6. key = 'foo'
  7. value = []
  8. data.setdefault(key, value)
  9. print('Before:', data)
  10. value.append('hello')
  11. print('After: ', data)
  • 在本例中,这就相当于每次调用时,不管字典里有没有这个键,都得分配一个 list 实例
  • 这有可能产生比较大的性能开销。

    回到第一个例子,为什么不用 setdefault

    ```python counters = { ‘pumpernickel’: 2, ‘sourdough’: 1, }

key = ‘dutch crunch’

counters.setdefault(key, 0) counters[key] += 1

print(counters) ```

  • 不管字典里有没有这个键,我们都要递增它所对应的值。
  • 在字典里没有这个键的情况下,这种写法会先通过 setdefault 把默认值 0 赋给这个键,然后再通过 counters[key] += 1 把递增之后的值更新到字典中,这其实完全没有必要。
  • 无论字典里有没有这个键

    • 之前那种 get 方案都只需要一次访问操作与一次赋值操作即可,
    • 而目前的 setdefault 方案(在字典里没有键的情况下) 需要一次访问操作与两次赋值操作

      总结

      只有在少数几种情况下,用 setdefault 处理缺失的键才是最简短的方式, 例如这种情况:与键相关联的默认值构造起来开销很低且可以变化,而且不用担心异常问题(例如 list 实例)。在这种特殊的场合,或许可以用这个名字有点儿奇怪的 setdefault 方法取代行数稍微多一些的 get 方案。 即便如此,一般也应该优先考虑用 defaultdict(带默认值的字典)取代 dict(参见第 17 条)。

  • 有四种办法可以处理键不在字典中的情况:in 表达式、KeyError 异常、get 方法与 setdefault 方法。

  • 如果跟键相关联的值是像计数器这样的基本类型,那么 get 方法就是最好的方案;如果是那种构造起来开销比较大,或是容易出异常的类型,那么可以把这个方法与赋值表达式结合起来使用。
  • 即使看上去最应该使用 setdefault 方案,也不一定要真的使用 setdefault 方案,而是可以考虑用 defaultdict 取代普通的 dict 。