🇯🇵 日本語 | 🇺🇸 English | 🇪🇸 Español | 🇵🇹 Português | 🇹🇭 ไทย | 🇨🇳 中文

告别KeyError烦恼!用Python的collections模块提升你的代码水平

要在电脑上通过命令提示符或 PowerShell 运行 Python,需要先下载并安装 Python。
如果你还没有安装,请参考Python安装与开发环境配置一文来完成安装。

大家好!我是那个靠着AI的帮助,仅用几个月编程经验就独立搭建了两个网站的“我”。

今天,我想结合我自己的失败经历,用简单易懂的方式,为大家讲解一个我在编程学习初期就“后悔没早点知道”的Python超实用功能——collections模块。

特别是对于那些曾被字典(`dict`)的`KeyError`错误困扰,或是感觉列表(`list`)处理速度有点慢的朋友们,这篇文章肯定能帮到你。读完本文,你的Python代码水平必将更上一层楼!我尽量避免使用专业术语,并准备了大量复制粘贴就能运行的代码,让我们一起体验“代码跑起来”的乐趣吧!


collections模块到底是什么?

一听到“模块”这个词,可能感觉有点难,但其实它就像“Python自带的一个方便的工具箱”。你不需要特地安装任何东西,只需写一行`import`就能立刻使用。

这个工具箱里装满了各种特殊的工具,它们让Python的基本数据结构,如`dict`(字典)和`list`(列表),变得更强大、更易用。这次,我将重点介绍其中两个让我惊呼“这个太好用了!”的工具:`defaultdict`和`deque`。


`defaultdict`:防止字典“手滑”出错的魔法盒子

没有`defaultdict`的日子太难了...(我的失败谈)

大家在使用字典(`dict`)时,有没有遇到过下面这种错误?

# 尝试访问普通字典中不存在的键
word_counts = {}
# 'apple'这个键还不存在
word_counts['apple'] += 1 # 这里会报错!

# 运行结果
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# KeyError: 'apple'

这就是`KeyError`错误,是Python在告诉你:“嘿,字典里没有这个键(key)哦!”。我在做网站时,就因为处理用户输入数据时遇到这个错误而头疼了好几个小时。

要避免这个错误,虽然可以事先用`if`语句检查键是否存在,或者使用`get()`方法,但这样代码会变得有点长,对吧?


救星`defaultdict`登场!

能一口气解决`KeyError`烦恼的就是`defaultdict`。它是一个神奇的字典,能够“在访问不存在的键时,自动为其创建一个‘初始值’”。

百闻不如一见,我们先来看代码。

from collections import defaultdict

# 通过指定int,创建一个初始值为0的defaultdict
word_counts = defaultdict(int)

# 'apple'这个键还不存在...但没关系!
print(f"访问前: {word_counts}") # 访问前是空的
word_counts['apple'] += 1 # 不会报错!它会自动创建 word_counts['apple'] = 0,然后再+1
print(f"访问后: {word_counts}")
print(f"apple的数量: {word_counts['apple']}")
print(f"orange的数量: {word_counts['orange']}") # 'orange'也不存在,但访问的瞬间会返回初始值0

很厉害吧!通过写`defaultdict(int)`,就等于在请求:“如果键不存在,就自动用`int()`,也就是`0`,来作为初始值吧。”这样一来,我们就可以不用担心`KeyError`,直接开始计算了。
这个特性在Python官方文档中也有说明,即“为不存在的键提供一个默认值”,是一个非常可靠的功能。


【即贴即用】应用示例:统计文章中单词出现的次数

`defaultdict`在需要计数的时候特别有用。例如,可以像下面这样轻松统计一篇文章中每个单词出现的次数:

from collections import defaultdict

sentence = "apple banana apple orange banana apple"
words = sentence.split() # 将句子分割成单词列表

# 指定int作为默认值的defaultdict
word_counts = defaultdict(int)

for word in words:
  word_counts[word] += 1

# 显示结果
for word, count in word_counts.items():
  print(f"'{word}': {count}次")

# 也可以转换成普通字典来查看内容
print(f"\n最终字典内容: {dict(word_counts)}")

请复制粘贴并运行这段代码。你会发现,即使没有一个`if`语句,它也能准确地统计出单词数量。这就是`defaultdict`的力量!


`deque`:解决列表“慢”问题的高速队列

列表其实不擅长在开头添加和删除

接下来要介绍的是`deque`(读作“deck”)。它和列表(`list`)很像,但有一个特点:在列表开头添加元素和从开头删除元素的速度极快

实际上,Python的`list`虽然擅长在末尾(右端)添加(`append`),但并不擅长在开头(左端)添加(`insert(0, ...)`)或删除(`pop(0)`)。

这是因为,当在列表开头添加或删除元素时,需要将之后的所有元素都向后移动一位。这就像很多人排队时,有人插队或排头的人离开,后面所有人都得跟着移动一样。元素越多,这个“移动”的工作就越繁重,处理速度也就越慢。


两端操作?交给`deque`就对了!

`deque`就是为了解决这个问题而生的。它的内部结构经过精心设计(使用了一种叫做双向链表的机制),使得在两端进行添加和删除时,无论元素有多少,都能瞬间完成。

我们还是通过代码来看看吧。在列表上进行的操作,用`deque`不仅方法名更直观,而且执行速度更快。

from collections import deque

# 创建一个deque
tasks = deque(['task2', 'task3'])
print(f"初始状态: {tasks}")

# 在末尾添加元素 (与list的append相同)
tasks.append('task4')
print(f"添加到末尾后: {tasks}")

# 在开头添加元素 (比list的insert(0,...)更快!)
tasks.appendleft('task1')
print(f"添加到开头后: {tasks}")

# 从开头删除元素 (比list的pop(0)更快!)
first_task = tasks.popleft()
print(f"从开头取出的任务: {first_task}")
print(f"当前状态: {tasks}")

【即贴即用】应用示例:最近浏览商品列表(有最大数量限制)

`deque`在管理任务或历史记录时非常方便。使用`maxlen`功能,可以轻松创建一个“始终只保留最新的N个项目”的列表。

想象一下电商网站的“最近浏览商品”功能。

from collections import deque
import time

# 创建一个最多保留5条历史记录的deque
history = deque(maxlen=5)

products = ['T恤', '运动鞋', '棒球帽', '连帽衫', '夹克', '短裤']

for product in products:
  print(f"正在浏览 '{product}'。")
  history.append(product)
  print(f"当前浏览历史: {list(history)}") # 转换为list()更便于查看
  time.sleep(1) # 等待1秒

print("\n--- 最终浏览历史 (最近5件) ---")
for item in history:
  print(item)

运行这段代码,你会看到每当有新商品添加时,最旧的商品就会被自动挤掉。如果用`list`自己实现这个功能,就需要`if len(list) > 5:`这样的检查,但用`deque(maxlen=N)`就完全不需要。是不是很巧妙?


【亲身体验】在浏览器中运行的单词计数器

为了让你能立刻体验到`defaultdict`的便利,我准备了一个可以在你的浏览器中运行的示例。请将下面的HTML代码完整复制,粘贴到文本编辑器中,并保存为“test.html”之类的文件名。然后,用浏览器打开这个文件吧。

虽然这不是Python代码,但它用JavaScript再现了`defaultdict`“即使键不存在也能自动初始化”的思路。请在文本框中输入任意文章,然后点击按钮试试看吧!

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>单词计数器体验</title>
  <style>
    body { font-family: sans-serif; background-color: #202124; color: #e8eaed; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
    .container { width: 90%; max-width: 600px; padding: 2rem; border: 1px solid #5f6368; border-radius: 8px; }
    h1 { color: #669df6; }
    textarea { width: 100%; height: 150px; background-color: #3c4043; color: #e8eaed; border: 1px solid #5f6368; border-radius: 4px; padding: 10px; font-size: 1rem; margin-bottom: 1rem; }
    button { background-color: #8ab4f8; color: #202124; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-weight: bold; }
    button:hover { opacity: 0.9; }
    #result { margin-top: 1.5rem; background-color: #282a2d; padding: 1rem; border-radius: 4px; }
    pre { white-space: pre-wrap; word-wrap: break-word; }
  </style>
</head>
<body>
  <div class="container">
    <h1>单词计数器</h1>
    <p>请在下方的框中输入文本,然后点击按钮。</p>
    <textarea id="text-input" placeholder="在此输入文本... (例如: apple banana apple orange)"></textarea>
    <button onclick="countWords()">计算单词数量</button>
    <div id="result">
      <pre>结果将显示在这里。</pre>
    </div>
  </div>

  <script>
    function countWords() {
      const text = document.getElementById('text-input').value;
      const resultEl = document.getElementById('result');

      if (!text.trim()) {
        resultEl.innerHTML = '<pre>尚未输入文本。</pre>';
        return;
      }

      // 通过空格或换行符分割单词,并移除空元素
      const words = text.toLowerCase().match(/\b(\w+)\b/g) || [];

      // 模拟defaultdict(int)的行为
      const counts = {}; 
      for (const word of words) {
        // 如果键不存在,则将其值设为0,然后递增
        counts[word] = (counts[word] || 0) + 1;
      }

      // 格式化并显示结果
      let resultText = '【计数结果】\n';
      for (const [word, count] of Object.entries(counts)) {
        resultText += `"${word}": ${count}次\n`;
      }
      
      resultEl.innerHTML = `<pre>${resultText}</pre>`;
    }
  </script>
</body>
</html>

使用`collections`时的注意事项

最后,我们来谈谈使用这些便利工具时需要注意的几个小地方。


总结:巧妙运用,让你的编程更上一层楼

这次,我们从Python的标准库“`collections`”模块中,介绍了两个尤其适合初学者了解的便利工具:`defaultdict`和`deque`。

自从我了解了这些工具后,我自己的代码变得更简洁,为错误烦恼的时间也大大减少了。大家也请先从复制粘贴本文的代码开始,亲身感受一下它们的便利吧。熟练使用基本的`list`和`dict`固然重要,但如果在合适的场景使用`collections`这样的便利工具,你就能写出更高效、更易读的代码!

下一步

体验了`collections`模块的便利之后,要不要挑战一下实际制作一个基于文本的应用程序呢?让我们来体验一下用基本数据结构创造“能动的东西”的乐趣吧!

用Python制作一个待办事项列表应用(文本版)