Effective Python 第16、17、18条:字典get方法和设置键默认值

介绍python字典中获取值的另一种方法,和设置键的默认值的方法。

get方法

在获取字典的值的时候,有一种方法是使用键中括号直接取值,如下:

d1 = {
    "a": "A",
    "b": "B",
    "c": "C",
}

print(d1["a"])

但是这种方法有个缺点是,一旦键不存在,方法会报错,除非提前判断键是否存在,如下:

print(d1["e"])
# KeyError: 'e'

使用get方法一样可以取到键对应的值,但是好处是如果键不存在,则不会报错,而是给一个默认值。没有指定的情况下就是None。如下:

d1 = {
    "a": "A",
    "b": "B",
    "c": "C",
}

# 不指定默认值
print(d1.get("e"))  # 结果为None
# 指定默认值
print(d1.get("e", ""))  # 结果为""

setdefault方法

setdefault方法会查询字典里有没有这个键,如果有,就返回对应的值,没有则把用户提供的默认值跟这个键关联并插入字典里。如下:

d1 = {
    "a": "A",
    "b": "B",
    "c": "C",
}

d1.setdefault("d", "")
print(d1)
# {'a': 'A', 'b': 'B', 'c': 'C', 'd': ''}

但是使用这个方法时要注意,如果默认值设置为空列表[],最好不要用变量引用,否则,容易出bug。如下:

d1 = {
    "a": [1],
    "b": [1, 2],
    "c": [1, 2, 3],
}
value = []
d1.setdefault("d", value)
d1.get("d", []).append(4)
print(d1)  # {'a': [1], 'b': [1, 2], 'c': [1, 2, 3], 'd': [4]}
print(value)  # [4]

value.append(5)
print(value)  # [4, 5]
print(d1)  # {'a': [1], 'b': [1, 2], 'c': [1, 2, 3], 'd': [4, 5]}

可以看到,当d1的"d"键的值变化,value变化跟着变化;value变化,d1的"d"键的值也跟着变化。这是因为d1的"d"键的列表和value都是引用同一个列表。

defaultdict 处理缺失值

假如字典是自己创建的,且需要字典实例来维护类对象的内部状态。如创建一个Visits类,记录去过哪些国家和这个国家的哪些城市,国家里的城市使用集合来存储,但是第一次记录国家时,还不一定有城市,就需要记个空集合。使用上一节学的setdefault,如下:

class Visits:
    def __init__(self):
        self.data = {}
    
    def add(self, country, city):
        city_set = self.data.setdefault(country, set())
        city_set.add(city)

这种做法不够高效,每次调用add方法,无论所指定的国家是否在字典里,都必须构造新的city_set 实例。

collections模块提供的defaultdict,可以在键缺失的情况下,自动添加这个键所对应的默认值。只需要在构造这种字典时提供一个函数,每次键不在,就调用这个函数返回一个新的默认值。

下面使用defaultdict改造Visits:

from collections import defaultdict


class Visits:
    def __init__(self):
        self.data = defaultdict(set)

    def add(self, country, city):
        self.data[country].add(city)


visit = Visits()
visit.add('England', 'Bath')
visit.add('England', 'London')
print(visit.data)
# defaultdict(<class 'set'>, {'England': {'London', 'Bath'}})

这样add方法相当简短,也不会每次调用add方法就分配一些set实例。

建议
1.如果自己管理的字典可能需要添加任意的键,那么优先考虑collections模块提供的defaultdict
2.如果字典是别人传的,你不知道键存不存在,应该优先使用get方法获取键值。个别情况下可以使用setdefault减少判断。

__missing__构造依赖键的默认值

如果接受一个文件路径,要读取文件,并把文件内容存起来。如下:

pictures = {}
path = 'p_1234.png'

if (handel := pictures.get(path)) is None:  # python3.8的赋值表达式
    try:
        handel = open(path, 'a+b')
    except OSError:
        print(f"Failed to open path {path}")
        raise 
    else:  # try执行成功会执行else
        pictures[path] = handel        
    
handel.seek(0)
image_data = handel.read()

python还内置了一些解决方案,可以通过继承dict类型并实现__missing__特殊方法解决这个问题。我们可以把字典里不存在这个键时所执行的逻辑写在这个方法。

下面定义一个新的类:

class Pictures(dict):
    
    def __missing__(self, key):
        value = self.open(key)
        self[key] = value
        return value
        
    def open_picture(self, path):
        try:
            return open(path, 'a+b')
        except OSError:
            print(f"Failed to open path {path}")
            raise
    

path = 'p_1234.png'
pictures = Pictures()
handel = pictures[path]
handel.seek(0)
image_data = handel.read()

访问pictures[path]时,如果pictures字典里没有path这个键,就会调用__missing_方法。这个方法根据key参数创建一份新的默认值,系统就会把这个默认值插入字典并返回给调用方。

建议:
1.传给defaultdict的函数必须是不需要参数的函数。
如果要构造的默认值必须根据键名来确定,那么可以定义自己的dict子类并实现__missing__方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ethan-running

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值