Python 字典键不存在用 get 而不是 try except

FreeGuideOnline 最新 2026-07-05

python data = {"name": "Alice", "age": 30}

try: city = data["city"] except KeyError: city = "Unknown"


这种方法依赖 Python 的 EAFP(Easier to Ask for Forgiveness than Permission,事后获取原谅比事前请求许可更容易)风格,逻辑上没问题。但当你只是想要一个默认值时,写四行代码显得过于冗余,并且引入了不必要的控制流。

### 2. `dict.get()` 方法:更加优雅的替代方案

`get(key, default)` 是字典对象的一个内置方法,它在键存在时返回对应值,不存在时返回指定的默认值(默认为 `None`)。上面的代码可以精简为:

```python
city = data.get("city", "Unknown")

这行代码只占一行,意图清晰:取键 "city" 的值,如果不存在则用 "Unknown"。它完美体现了 Python 的简洁风格。

3. 为什么 get() 优于 try/except

(1)可读性和简洁性更高

对比两段代码,get 版本一眼就能看出包含默认值逻辑,而 try/except 需要多行才能理解。对于初学者和团队成员而言,get() 降低了认知负担。

(2)避免了不必要的异常处理开销

尽管 Python 中异常处理的成本在现代解释器中已大幅优化,但当 KeyError 真正被抛出时,仍然会产生栈跟踪和异常传播的额外开销。如果你在循环或高频操作中频繁使用 try/except 且异常真的频繁发生,性能会受到影响。

get() 直接在 C 层面完成键的检查与返回,不会触发异常机制,因此在“键经常缺失”的场景下效率更高。

(3)更符合常见业务语义

大多数时候我们真的只想要“存在则取值,不存在则给个默认值”,并不需要“捕获异常再做其他复杂处理”。这时 get() 是对意图的直接映射。try/except 更适合需要根据异常类型执行不同清理或恢复操作的复杂场景,而简单的默认值赋值用 get() 更恰当。

(4)支持链式操作和函数式风格

get() 返回一个值,可以方便地用于表达式或与 orand 结合。例如:

# 尝试取配置值,没有则取环境变量,再没有则用默认值
config = settings.get("timeout") or os.environ.get("TIMEOUT", 30)

如果用 try/except,你不得不写嵌套或临时变量,降低流畅度。

4. get() vs try/except 性能对比简析

下面通过简单的 timeit 测试展示两者在“键存在”和“键不存在”两种情形下的性能差异(在 Python 3.11 中测试,结果因版本和环境而异,但趋势一致):

import timeit

setup = "data = {'key': 'value'}; default = 'default'"

# 键存在的 case
t1 = timeit.repeat("try: val = data['key']\nexcept KeyError: val = default", setup=setup, number=10**6)
t2 = timeit.repeat("val = data.get('key', default)", setup=setup, number=10**6)

# 键不存在的 case
t3 = timeit.repeat("try: val = data['missing']\nexcept KeyError: val = default", setup=setup, number=10**6)
t4 = timeit.repeat("val = data.get('missing', default)", setup=setup, number=10**6)

print(f"键存在   try/except: {min(t1):.4f}s, get: {min(t2):.4f}s")
print(f"键不存在 try/except: {min(t3):.4f}s, get: {min(t4):.4f}s")

典型输出:

键存在   try/except: 0.1312s, get: 0.1087s
键不存在 try/except: 0.3521s, get: 0.1054s
  • 键存在时get() 略快一些,因为避免了异常路径。
  • 键缺失时get() 几乎与存在时一样快,而 try/except 却慢了 3 倍以上,因为真正的异常抛出成本显著。

因此,如果业务中键缺失的概率较高,get() 带来的性能优势不可忽视。

5. get() 的常见使用模式与最佳实践

(1)提供安全的默认值

user = {"name": "Bob"}
print(user.get("email", "no email provided"))
# 输出: no email provided

(2)作为轻量级的条件判断

if user.get("is_admin"):
    grant_access()

这里利用 get() 默认返回 None(假值),无需额外判断键是否存在。

(3)嵌套字典的安全取值

需要多层访问时,可配合 get 链式调用或使用 setdefault,但注意链式 get 需要保证中间层不为 None,否则会抛出 AttributeError。例如:

config = {"database": {"host": "localhost"}}
host = config.get("database", {}).get("host", "default_host")

(4)计数与累加器

get() 常用来实现计数器或字典分组:

words = ["apple", "banana", "apple"]
count = {}
for w in words:
    count[w] = count.get(w, 0) + 1
print(count)  # {'apple': 2, 'banana': 1}

Python 还提供了 collections.defaultdictCounter 用于更高级的计数需求,但 get() 是实现基本原理的首选。

6. 什么时候依然需要使用 try/except

get() 并非万能。以下场景中 try/except 更合适:

  • 你需要在异常发生时执行复杂的恢复逻辑,而不仅仅是返回一个静态默认值。例如记录日志、重试操作、清理资源等。
  • 你需要区分“键不存在”和“值为 None”的情况。get() 无法区分这两种,因为它只返回默认值。而 try/except 中可以在 except KeyError 分支里明确处理缺失,在 else 分支中处理值。
  • 键的取值本身可能引发其他异常,而你希望统一捕获处理。

示例:

try:
    coord = data["location"]["coordinates"]
except KeyError:
    coord = None
    log.warning("location key missing")