字典这种数据结构, 它在日常的开发工作之中是使用频率特别高的一个东西, 可是你觉得自己真的已经把它用得特别顺手了吗, 这篇文章把我们整理出来的15个比较高级的使用方式给列出来了, 它会带着你从比较基础的知识一直学到更进阶的內容, 这样一来就能够让你的字典操作效率直接提升十倍。
一、我们要回过头去把基础的知识再看一遍, 看看到底是有没有5种方式能够用来创建一个字典。
# ① 字面量(最常用)d1 = {"name": "张三", "age": 25}
# ② dict()构造函数
d2 = dict(name="李四", age=30)
# ③ fromkeys() —— 批量创建相同值的键
d3 = dict.fromkeys(["a", "b", "c"], 0) # {'a': 0, 'b': 0, 'c': 0}
# ④ 字典推导式(高级!
)
d4 = {x: x*x for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# ⑤ zip()组合两个列表
keys = ["name", "age", "city"]
values = ["王五", 28, "上海"]
d5 = dict(zip(keys, values)) # {'name': '王五', 'age': 28, 'city': '上海'}
二、详细解释一下15个高级用法, 第一个是用法1: get()这个函数是用来安全地取值的, 目的是为了在操作过程中避免出现错误或者意外的情况。
user = {"name": "张三", "age": 25}# 直接用[]访问,键不存在会崩溃
# print(user["gender"]) # KeyError!
# get() 安全取值,不存在返回默认值
print(user.get("gender")) # None
print(user.get("gender", "未知")) # "未知"
实战场景:统计词频
words = ["apple", "banana", "apple", "orange", "banana", "apple"]counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts) # {'apple': 3, 'banana': 2, 'orange': 1}
用法2: 括号里填上相关内容——如果查询不到, 那就进行设置。
words = ["apple", "banana", "apricot", "blueberry", "cherry"]groups = {}
for word in words:
key = word[0].upper()
groups.setdefault(key, []).append(word)
print(groups) # {'A': ['apple', 'apricot'], 'B': ['banana', 'blueberry'], 'C': ['cherry']}
关于 vs get() 之间的区别问题, 我们可以直接说清楚, get() 这个函数方法, 它只是单纯地读取内容, 是完全不去修改任何东西的, 括号里面是空的, 当读不到数据的时候, 它就会把默认值写入进去。
在用法3里面, 这个符号()是用来表示合并字典的意思的。
defaults = {"theme": "light", "font_size": 14, "lang": "zh"}user_prefs = {"theme": "dark", "lang": "en"}
settings = defaults.copy()
settings.update(user_prefs)
print(settings) # {'theme': 'dark', 'font_size': 14, 'lang': 'en'}
# Python 3.9+ 新语法:| 运算符合并
merged = defaults | user_prefs
defaults |= {"font_size": 18} # 原地更新
当使用到第四种用法的时候, 也就是字典推导式这种方法, 仅仅通过一行代码就能够把比较复杂的任务完成转换。
# 翻转字典original = {"a": 1, "b": 2, "c": 3}
flipped = {v: k for k, v in original.items()} # {1: 'a', 2: 'b', 3: 'c'}
# 过滤字典
scores = {"张三": 85, "李四": 92, "王五": 58, "赵六": 77}
passed = {k: v for k, v in scores.items() if v >= 60} # {'张三': 85, '李四': 92, '赵六': 77}
# 值转换
prices = {"apple": 3.5, "banana": 2.0, "orange": 4.0}
discounted = {k: round(v * 0.9, 2) for k, v in prices.items()}
关于使用方法的五种情况中, 有一个是用到了items()函数、keys()函数, 以及那个只留了括号没写字的()这种写法, 这三种方式都是用来进行遍历操作的。
student = {"name": "张三", "age": 25, "city": "北京"}for key, value in student.items(): # 遍历键值对
print(f"{key} = {value}")
for key in student.keys(): # 只遍历键
print(key)
for value in student.values(): # 只遍历值
print(value)
用法6:pop()——取出并删除
config = {"host": "localhost", "port": 3306, "password": "123456", "debug": True}password = config.pop("password", "") # 取出并移除
debug_mode = config.pop("debug", False)
print(config) # password和debug已被移除
用法部分是7, 这里的点号保留, 意思是关于自带默认值的字典这个东西。
from collections import defaultdictdd = defaultdict(list)
for word in ["a", "b", "a", "c", "b", "a"]:
dd[word].append(1)
print(dd) # defaultdict(, {'a': [1, 1, 1], 'b': [1, 1], 'c': [1]})
counter = defaultdict(int)
for char in "hello world":
counter[char] += 1
print(counter['z']) # 0 —— 不存在的键也返回0!
用法部分是8, 这里提到的内容是关于有序字典的。
from collections import OrderedDictdata = {"banana": 3, "apple": 5, "cherry": 2}
sorted_by_value = OrderedDict(sorted(data.items(), key=lambda x: x[1]))
od = OrderedDict([('a', 1), ('b', 2)])
od.move_to_end('b') # 移到最后
在用法九里面, 大家可以用链式查找的方式去处理多个字典的情况。
from collections import ChainMapcmd_args = {"verbose": True}
env_vars = {"color": "auto"}
defaults = {"verbose": False, "color": "never"}
config = ChainMap(cmd_args, env_vars, defaults)
print(config["verbose"]) # True —— 来自cmd_args(最高优先级)
print(config["color"]) # "auto" —— 来自env_vars
用法10:深层嵌套字典的安全访问
data = {"user": {"profile": {"name": "张三", "address": {"city": "北京"}}}}city = data.get("user", {}).get("profile", {}).get("address", {}).get("city", "未知")
print(city) # 北京
def deep_get(d, keys, default=None):
for key in keys:
if isinstance(d, dict): d = d.get(key, default)
else: return default
return d
print(deep_get(data, ["user", "profile", "address", "city"])) # 北京
在第11个用法当中, 把JSON格式转换到字典里面去或者相反。
import jsondata = {"name": "张三", "skills": ["Python", "SQL"]}
json_str = json.dumps(data, ensure_ascii=False, indent=2)
parsed = json.loads(json_str)
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open("data.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
方法第十二种, 它就是一个用来计算个数的非常厉害的宝贝。
from collections import Countertext = "python is great and python is easy"
word_counts = Counter(text.split())
print(word_counts.most_common(3)) # [('python', 2), ('is', 2), ('and', 1)]
nums = [1, 2, 3, 2, 4, 5, 3, 6, 3]
dupes = [item for item, count in Counter(nums).items() if count > 1]
print(dupes) # [2, 3]
c1 = Counter(a=3, b=1); c2 = Counter(a=1, b=2, c=4)
print(c1 + c2) # Counter({'a': 4, 'b': 3, 'c': 4})
print(c1 & c2) # Counter({'a': 1, 'b': 1})
第13种用法是使用字典进行排序。
students = {"张三": 85, "李四": 92, "王五": 78, "赵六": 96}desc = dict(sorted(students.items(), key=lambda x: x[1], reverse=True))
top_3 = dict(sorted(students.items(), key=lambda x: x[1], reverse=True)[:3])
print(top_3) # {'赵六': 96, '李四': 92, '钱七': 88}
用法14:字典扁平化与嵌套化
def nest_dict(flat_dict, sep="."):result = {}
for key, value in flat_dict.items():
parts = key.split(sep)
current = result
for part in parts[:-1]:
if part not in current: current[part] = {}
current = current[part]
current[parts[-1]] = value
return result
flat = {"db.host": "localhost", "db.port": 3306, "app.name": "MyApp"}
nested = nest_dict(flat)
# {'db': {'host': 'localhost', 'port': 3306}, 'app': {'name': 'MyApp'}}
在进行性能优化方面的时候, 我们会用到这些叫做技巧的东西。
def fast_lookup(data_dict, keys):get = data_dict.get # 缓存方法引用
return [get(key) for key in keys]
# 用setdefault/get替代in检查(一次查找搞定两次的事)
value = d.get("key", default) # 快:一次查找
三、综合实战:INI配置文件解析器
from collections import defaultdictclass ConfigParser:
def __init__(self):
self.data = defaultdict(dict)
self.global_section = {}
def load(self, filename):
current = "__global__"
with open(filename, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or line.startswith(";"): continue
if line.startswith("[") and line.endswith("]"):
current = line[1:-1]; continue
if "=" in line:
key, _, value = line.partition("=")
key, value = key.strip(), value.strip()
if current == "__global__": self.global_section[key] = value
else: self.data[current][key] = value
def get(self, section, key, default=None):
if section == "__global__": return self.global_section.get(key, default)
return self.data[section].get(key, default)
def sections(self): return list(self.data.keys())
config_content = """\
app_name = My Application
version = 1.0.0
[database]
host = localhost
port = 3306
[server]
port = 8080
"""
with open("test.ini", "w", encoding="utf-8") as f: f.write(config_content)
parser = ConfigParser()
parser.load("test.ini")
print(parser.get("database", "host")) # localhost
print(parser.sections()) # ['database', 'server']
四、速查表
总结一下, 字典是内置数据结构里面最强大的那一个。如果你能够熟练地掌握关于键获取、列表推导式之类的这些用法的话, 那么你的代码一定会变得更加简洁、更加安全、也更加高效。
关于核心记忆方面, 需要记住的是使用 get() 方法可以防止程序崩溃, 使用空括号的方式进行重复判断能够加以避免, 通过使用推导式在单行里面实现转换操作可以节省代码的编写量, 以及利用这些技巧来做数据统计工作。
如果认为这个内容有用, 就进行点赞和收藏操作, 到了下一期节目再详细讲解《生成器yield详解——一行代码省1GB内存》的相关知识与技术细节。