欢迎光临
我们一直在努力

Python 高级编程 028:字典dict从入门到精通

Python 高级编程 028:字典dict从入门到精通

  • 📌 写在前面:为什么dict是Pythoner的必修课?
  • Bilibili 同步视频
  • 🧬 第一部分:dict的"血统"探秘——抽象基类ABC继承关系
    • 1.1 两大抽象基类:Mapping & MutableMapping
    • 1.2 isinstance判断的"坑":类 vs 实例
    • 1.3 底层揭秘:不是继承,是"注册"!
  • 🛠️ 第二部分:dict常用方法大全(核心干货!)
    • 2.1 clear():一键清空,干干净净
    • 2.2 copy():深浅拷贝的那些"坑",90%的人都踩过!
      • 什么是浅拷贝?
      • 深拷贝:彻底解决嵌套问题
    • 2.3 fromkeys():批量造字典神器
    • 2.4 get():告别KeyError的安全访问方式
      • 为什么需要get?
      • get的正确打开方式
    • 2.5 items() / keys() / values():遍历三件套
    • 2.6 setdefault():取值+赋值一步到位,效率更高!
      • setdefault做了什么?
      • setdefault的经典应用场景
    • 2.7 update():字典合并的N种姿势
      • 姿势一:传入另一个字典
      • 姿势二:关键字参数传参
      • 姿势三:传入嵌套元组的可迭代对象
  • ⚡ 第三部分:dict性能小Tips,让你的代码飞起来!
    • 3.1 优先使用内置方法,少自己写逻辑
    • 3.2 字典推导式 vs 循环赋值
    • 3.3 深浅拷贝的选择
    • 3.4 大字典的key查询
  • 📝 总结:dict方法速查表
  • 🎯 写在最后

📌 写在前面:为什么dict是Pythoner的必修课?

哈喽各位Python小伙伴们~ 👋 今天咱们来聊一聊Python中最最最常用的数据结构之一——dict(字典)!

说到dict啊,那可真是Python界的"万能工具箱"🧰,不管是存配置、做缓存、转JSON、统计计数,几乎没有它干不了的活儿。但是!你真的完全搞懂dict了吗?

灵魂拷问三连:

  • dict到底继承自谁?它和list是"远房亲戚"吗?🤔

  • 浅拷贝和深拷贝到底有啥区别?为啥改了新字典旧的也跟着变?😱

  • setdefault和get到底差在哪?update居然有三种写法?🤯

别慌!今天这篇文章,咱们就从底层继承关系讲到全部常用方法,再配上性能小Tips,保证你看完之后,dict用得那叫一个炉火纯青!🔥 坐稳扶好,咱们发车啦~ 🚀


Bilibili 同步视频

Python 高级编程 028:字典dict从入门到精通


🧬 第一部分:dict的"血统"探秘——抽象基类ABC继承关系

1.1 两大抽象基类:Mapping & MutableMapping

首先,咱们得搞清楚一个问题:Python的dict,它到底是个啥"出身"?

在Python的 collections.abc 模块里,藏着两个非常重要的抽象基类——Mapping 和 MutableMapping。它们就像是dict的"族谱"上的两位老祖宗~ 👴👵

from collections.abc import Mapping, MutableMapping

咱们来梳理一下这俩的关系:

抽象基类核心特性代表方法
Mapping 只读映射,只能查不能改 get、keys、items、values、getitem
MutableMapping 可变映射,可读可写可删 pop、update、setdefault、clear、setitem

而咱们的 dict,它就属于 MutableMapping 类型!毕竟字典嘛,肯定得能增删改查才行~

1.2 isinstance判断的"坑":类 vs 实例

说到类型判断,这里有个非常经典的小坑,很多新手小伙伴都踩过!🕳️

❌ 错误写法:直接把dict类传进去

# 这样写是错的!返回 False
print(isinstance(dict, MutableMapping)) # False

为啥是False呢?因为 isinstance() 判断的是实例的类型,而不是类本身!dict是一个类,不是实例,当然不对啦~

✅ 正确写法:创建一个字典实例再判断

a = {"name": "imock"}
print(isinstance(a, MutableMapping)) # True

划重点!isinstance第一个参数必须是实例对象,不是类!这个坑记好了,面试常考哦~ 🎯

1.3 底层揭秘:不是继承,是"注册"!

说到这里,可能有小伙伴会问:“那dict是继承了MutableMapping吗?” 哎,这个问题问得好!👍

答案是——并没有! 😮

Python的dict底层是用C语言写的,它并没有在语法层面去继承MutableMapping这个抽象基类。那为什么isinstance还能返回True呢?

秘密就在于Python的**注册机制(register)和classhook**钩子函数!

简单来说就是:dict虽然没有直接继承,但是它实现了MutableMapping协议中规定的所有魔法函数(比如__getitem__、setitem、__len__等等),然后通过register机制"登记在册",所以isinstance就认它是MutableMapping的"自己人"啦~ 🤝

**一句话总结:**dict和list都实现了顶层的Collection协议,所以它们有很多通用操作;但dict额外实现了Mapping协议,拥有键值对的专属能力。这就是Python的"鸭子类型"哲学——走起来像鸭子,叫起来像鸭子,那它就是鸭子!🦆


🛠️ 第二部分:dict常用方法大全(核心干货!)

好了,血统搞清楚了,接下来咱们进入重头戏——dict的那些实用方法!💪

很多小伙伴觉得dict不就是增删改查吗?那你可就太小瞧它了!Python的dict藏着很多"黑科技",用好了能让你的代码既优雅又高效~

2.1 clear():一键清空,干干净净

第一个方法非常简单——clear(),顾名思义,就是清空字典里所有的键值对。

# 创建一个字典
a = {"bob1": "imock", "bob2": "imock2"}

# 清空字典
a.clear()

print(a) # {}

⚠️ 注意:clear()是原地修改,没有返回值(或者说返回None)。它直接把原字典清空了,不会给你返回一个新的空字典哦~

2.2 copy():深浅拷贝的那些"坑",90%的人都踩过!

接下来这个方法——copy(),看似简单,实则暗藏玄机!这绝对是dict方法中坑最多的一个,没有之一!😤

什么是浅拷贝?

dict.copy() 返回的是一个浅拷贝(shallow copy)。什么意思呢?

简单来说:

  • ✅ 顶层的键值对会复制一份,新字典和原字典是独立的容器

  • ❌ 但是!如果值是嵌套的可变对象(比如内层的dict、list),只会复制引用,不会复制内容!

说人话就是:外壳是新的,里面的东西还是原来的那些~ 📦

# 原字典,值是嵌套的字典
a = {"bob1": {"company": "imock"}}

# 浅拷贝
new_dict = a.copy()

# 修改新字典里的嵌套字典
new_dict["bob1"]["company"] = "imock3"

# 猜猜看,原字典a变了吗?
print(a) # {'bob1': {'company': 'imock3'}}
# 啊!原字典居然也变了!😱

**坑点预警!**浅拷贝只拷贝了最外层,内层的可变对象还是共享同一份内存。改一个,另一个也跟着变,这个bug能让你找半天!

深拷贝:彻底解决嵌套问题

那如果我想要完全独立的拷贝怎么办?这时候就需要**深拷贝(deep copy)**了!

深拷贝会递归地拷贝所有层级的数据,完完全全复制一份,新的和旧的彻底没关系,老死不相往来~ 👋

import copy

a = {"bob1": {"company": "imock"}}

# 深拷贝
new_dict = copy.deepcopy(a)

# 修改新字典
new_dict["bob1"]["company"] = "imock3"

# 原字典不受影响!
print(a) # {'bob1': {'company': 'imock'}}
print(new_dict) # {'bob1': {'company': 'imock3'}}

**性能小Tips:**深拷贝因为要递归遍历所有数据,所以性能比浅拷贝差很多。如果你的字典里没有嵌套的可变对象,用浅拷贝就够了,又快又香~ 只有在确实需要完全隔离的时候,才用深拷贝。

2.3 fromkeys():批量造字典神器

接下来介绍一个非常好用的静态方法——fromkeys()!

想象一下这个场景:你有一个列表,里面是一堆key,你想把它们都变成字典的键,并且统一赋一个默认值。怎么办?循环遍历一个个加?太low了!👎

用 dict.fromkeys(),一行搞定!✨

# 有一个key列表
key_list = ["bob1", "bob2", "bob3"]

# 批量创建字典,所有key的默认值都是"imock"
new_dict = dict.fromkeys(key_list, "imock")

print(new_dict)
# {'bob1': 'imock', 'bob2': 'imock', 'bob3': 'imock'}

语法:dict.fromkeys(可迭代对象, 默认值)

第一个参数可以是任意可迭代对象——list、tuple、set、甚至另一个字典的keys,统统都可以!非常灵活~ 🎯

**注意坑点:**如果默认值是可变对象(比如空列表[]),所有的key都会共享同一个对象!改一个全变。这时候建议用字典推导式或者循环来创建。

2.4 get():告别KeyError的安全访问方式

接下来这个方法——get(),可以说是dict方法中使用频率最高的一个了!强烈建议所有人都掌握!🔥

为什么需要get?

咱们平时访问字典,最常用的就是方括号语法:

d = {"name": "imock"}
print(d["name"]) # 正常
print(d["age"]) # KeyError!💥

但是!如果key不存在,直接就给你抛个 KeyError,程序直接崩给你看~ 😭 在实际开发中,这可是高频崩溃来源之一!

get的正确打开方式

用 get() 就安全多了:key存在就返回对应的值,不存在就返回默认值,绝对不报错!

d = {"name": "imock"}

# key存在,正常返回值
print(d.get("name", "默认值")) # imock

# key不存在,返回默认值,不报错
print(d.get("age", "未知")) # 未知

# 不传默认值的话,key不存在返回None
print(d.get("gender")) # None

**最佳实践:**凡是你不确定key是否存在的场景,一律用get()!尤其是处理接口返回数据、用户输入等不可信数据源时,get()能帮你规避99%的KeyError。写代码稳如狗~ 🐶

2.5 items() / keys() / values():遍历三件套

这三个方法大家应该都很熟悉了,遍历字典的时候天天用~

d = {"name": "imock", "age": 18, "city": "北京"}

# 遍历键值对(最常用!)
for k, v in d.items():
print(f"{k}: {v}")

# 只遍历键
for k in d.keys():
print(k)

# 只遍历值
for v in d.values():
print(v)

这里有个小知识点:items()返回的是元组拆包的形式,所以我们可以直接用 for k, v in d.items() 这种写法,非常优雅~

2.6 setdefault():取值+赋值一步到位,效率更高!

接下来这个方法——setdefault(),可以说是dict方法中的宝藏方法!很多人都不知道它,但它真的超级好用!💎

setdefault做了什么?

简单来说,setdefault干了两件事:

  • 先查找key,如果存在,直接返回对应的值(和get一样)

  • 如果key不存在——不仅返回默认值,还会把这个key和默认值存进字典里!

  • 对比一下:

    方法key不存在时返回默认值key不存在时写入字典
    get() ✅ 是 ❌ 否
    setdefault() ✅ 是 ✅ 是

    d = {"name": "imock"}

    # key不存在,返回默认值,并且写入字典
    result = d.setdefault("age", 18)

    print(result) # 18
    print(d) # {'name': 'imock', 'age': 18}

    # key已存在,直接返回已有值,不修改
    result2 = d.setdefault("name", "其他值")
    print(result2) # imock
    print(d) # {'name': 'imock', 'age': 18} 没变

    setdefault的经典应用场景

    setdefault最经典的应用就是分组统计!比如你想把一个列表按某个条件分组,存到字典里:

    # 传统写法(啰嗦版)
    result = {}
    for item in data_list:
    key = item["category"]
    if key not in result:
    result[key] = []
    result[key].append(item)

    # setdefault写法(优雅版)
    result = {}
    for item in data_list:
    key = item["category"]
    result.setdefault(key, []).append(item)

    **性能小Tips:**setdefault是Python内置的C级实现,比你自己写"if判断+赋值"的步骤更少,性能更高!尤其是在大数据量循环中,差距会更明显。又优雅又快,何乐而不为呢~ 😎

    2.7 update():字典合并的N种姿势

    最后,咱们来聊聊 update() 方法——字典合并神器!

    update的作用就是把另一个字典(或者可迭代对象)的键值对,合并更新到当前字典里。key相同的话,新值会覆盖旧值。

    但是!你知道update有三种传参方式吗?很多人只知道第一种~ 🤫

    姿势一:传入另一个字典

    d = {"name": "imock"}
    d.update({"age": 18, "city": "北京"})
    print(d) # {'name': 'imock', 'age': 18, 'city': '北京'}

    姿势二:关键字参数传参

    d = {"name": "imock"}
    d.update(age=18, city="北京", gender="男")
    print(d)
    # {'name': 'imock', 'age': 18, 'city': '北京', 'gender': '男'}

    这种写法是不是更简洁?直接写key=value就行,不用加引号、不用写大括号,爽得很~ 🎉

    姿势三:传入嵌套元组的可迭代对象

    d = {"name": "imock"}

    # 列表包元组
    d.update([("age", 18), ("city", "北京")])

    # 元组包元组也行
    d.update((("gender", "男"), ("job", "程序员")))

    print(d)
    # {'name': 'imock', 'age': 18, 'city': '北京', 'gender': '男', 'job': '程序员'}

    为啥这种方式也行呢?因为update的本质是遍历一个可迭代对象,然后把每个元素当成(key, value)对来处理。只要是可迭代的,里面装的是二元组,统统都可以!

    **Python哲学:**这就是Python的"鸭子类型"思想的体现——不关心你是什么类型,只关心你能不能迭代、能不能拆成两个元素。灵活得很~ 🦆


    ⚡ 第三部分:dict性能小Tips,让你的代码飞起来!

    聊了这么多方法,最后给大家分享几个dict的性能优化小技巧,都是干货!📦

    3.1 优先使用内置方法,少自己写逻辑

    Python的dict内置方法(get、setdefault、update等)都是用C语言实现的,比你自己用Python写if判断、循环赋值快得多。

    # 慢!Python层的判断+赋值
    if key not in d:
    d[key] = value
    val = d[key]

    # 快!C语言级别的内置实现
    val = d.setdefault(key, value)

    3.2 字典推导式 vs 循环赋值

    创建字典的时候,优先用字典推导式,比循环append快不少:

    # 推荐:字典推导式
    result = {k: v for k, v in data if condition}

    # 不推荐:循环赋值
    result = {}
    for k, v in data:
    if condition:
    result[k] = v

    3.3 深浅拷贝的选择

    • 没有嵌套可变对象 → 用 copy() 浅拷贝,快

    • 有嵌套且需要完全隔离 → 用 copy.deepcopy() 深拷贝,安全但慢

    • 只是想传参避免修改原字典 → 考虑是否真的需要拷贝,很多时候不需要

    3.4 大字典的key查询

    dict的key查询是O(1)的哈希查找,非常快。但如果你要反复判断某个key是否存在,用 in 操作符就够了,不要傻乎乎地遍历keys():

    # ✅ 推荐:O(1) 哈希查找
    if key in d:
    ...

    # ❌ 不推荐:O(n) 遍历,蠢
    if key in d.keys():
    ...


    📝 总结:dict方法速查表

    最后,给大家整理了一张dict常用方法速查表,建议收藏备用~ 📌

    方法作用推荐指数
    clear() 清空字典所有键值对 ⭐⭐⭐
    copy() 浅拷贝字典(嵌套对象共享引用) ⭐⭐⭐⭐
    fromkeys() 用可迭代对象批量创建字典 ⭐⭐⭐
    get() 安全取值,key不存在返回默认值 ⭐⭐⭐⭐⭐
    items() 返回所有键值对,用于遍历 ⭐⭐⭐⭐⭐
    keys() 返回所有键 ⭐⭐⭐⭐
    values() 返回所有值 ⭐⭐⭐⭐
    pop() 删除指定key并返回值 ⭐⭐⭐⭐
    setdefault() 取值+不存在则写入,一步到位 ⭐⭐⭐⭐⭐
    update() 合并更新字典(三种传参方式) ⭐⭐⭐⭐⭐

    🎯 写在最后

    好啦,今天关于Python dict的分享就到这里啦~ 🎉

    回顾一下,咱们从dict的抽象基类继承关系讲起,深挖了isinstance的坑,揭秘了注册机制的底层原理;然后详细讲解了clear、copy、fromkeys、get、setdefault、update等核心方法,还特别强调了浅拷贝深拷贝这个经典坑点;最后还给大家分享了几个性能优化小技巧。

    怎么样?是不是感觉对dict的理解又深了一层?😎

    **互动时间:**你在使用dict的过程中还遇到过哪些坑?或者有什么独门绝技?欢迎在评论区留言分享,大家一起交流进步~ 👇

    Python 高级编程 028:字典dict从入门到精通

    如果这篇文章对你有帮助,别忘了 点赞👍 + 收藏⭐ + 关注👀 三连支持一下哦!你们的支持就是我更新的最大动力~ 笔芯!❤️

    我是郝学胜,一个专注于Python技术分享的程序员。咱们下篇文章再见~ 👋👋👋

    赞(0)
    未经允许不得转载:171主机测评 » Python 高级编程 028:字典dict从入门到精通
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址