Skip to main content

Python 常用内置模块

Python 内置模块 collections

collections 模块是 Python 的内置模块,提供了许多有用的集合类型,这些类型是对 Python 基本数据类型(如 dict、list、set 和 tuple)的扩展和增强。该模块中的类型能够帮助我们更高效地处理数据结构相关的任务。下面请看对 collections 模块的详细介绍。

import collections
print(dir(collections))

Counter - 计数器

Counter 基本用法

Counter 是一个字典的子类,用于计算可哈希对象的数量。它是一个无序的集合,其中元素存储为字典的键,它们的计数存储为值。

from collections import Counter

# 统计字符串中每个字符的出现次数
text = "hello world"
char_count = Counter(text)
print(char_count)  # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

# 统计列表中每个元素的出现次数
fruits = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
fruit_count = Counter(fruits)
print(fruit_count)  # Counter({'apple': 3, 'banana': 2, 'cherry': 1})

Counter 的创建方式

from collections import Counter

# 1. 从可迭代对象创建
counter1 = Counter([1, 2, 3, 1, 2, 1])
print("从列表创建:", counter1)

# 2. 从字典创建
counter2 = Counter({'a': 3, 'b': 2, 'c': 1})
print("从字典创建:", counter2)

# 3. 从关键字参数创建
counter3 = Counter(cats=4, dogs=2, birds=1)
print("从关键字参数创建:", counter3)

# 4. 创建空的 Counter
counter4 = Counter()
print("空 Counter:", counter4)

Counter 的常用方法

from collections import Counter

# 创建一个 Counter 对象
votes = Counter(['A', 'B', 'C', 'A', 'B', 'A', 'C', 'A', 'B'])

# most_common() - 返回最常见的元素和它们的计数
print("最常见的2个元素:", votes.most_common(2))  # [('A', 4), ('B', 3)]
print("所有元素按频率排序:", votes.most_common())

# elements() - 返回一个迭代器,包含所有元素
print("所有元素:", list(votes.elements()))

# total() - 返回所有计数的总和 (Python 3.10+)
# print("总计数:", votes.total())

# subtract() - 减去计数
votes.subtract(['A', 'B'])
print("减去后的计数:", votes)

# update() - 增加计数
votes.update(['A', 'A', 'D'])
print("增加后的计数:", votes)

Counter 的数学运算

from collections import Counter

counter1 = Counter(['a', 'b', 'c', 'a', 'b', 'b'])
counter2 = Counter(['a', 'b', 'b', 'd'])

print("counter1:", counter1)  # Counter({'b': 3, 'a': 2, 'c': 1})
print("counter2:", counter2)  # Counter({'b': 2, 'a': 1, 'd': 1})

# 加法 - 合并计数
print("加法:", counter1 + counter2)  # Counter({'b': 5, 'a': 3, 'c': 1, 'd': 1})

# 减法 - 减去计数(保留正数)
print("减法:", counter1 - counter2)  # Counter({'b': 1, 'a': 1, 'c': 1})

# 交集 - 取最小值
print("交集:", counter1 & counter2)  # Counter({'a': 1, 'b': 2})

# 并集 - 取最大值
print("并集:", counter1 | counter2)  # Counter({'b': 3, 'a': 2, 'c': 1, 'd': 1})

defaultdict - 默认字典

defaultdict 基本概念

defaultdict 是字典的子类,当访问不存在的键时,会自动创建该键并赋予默认值。默认值由传入的工厂函数生成。

from collections import defaultdict

# 使用 list 作为默认工厂函数
dd_list = defaultdict(list)
dd_list['fruits'].append('apple')
dd_list['fruits'].append('banana')
dd_list['vegetables'].append('carrot')

print(dd_list)  # defaultdict(<class 'list'>, {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']})
print(dd_list['meat'])  # [],自动创建空列表

不同类型的默认值

from collections import defaultdict

# 使用 int 作为默认值(默认为0)
dd_int = defaultdict(int)
text = "hello world"
for char in text:
    dd_int[char] += 1
print("字符计数:", dict(dd_int))

# 使用 set 作为默认值
dd_set = defaultdict(set)
dd_set['numbers'].add(1)
dd_set['numbers'].add(2)
dd_set['letters'].add('a')
print("集合字典:", dict(dd_set))

# 使用 str 作为默认值
dd_str = defaultdict(str)
dd_str['greeting'] += 'Hello'
dd_str['greeting'] += ' World'
print("字符串字典:", dict(dd_str))

自定义默认值函数

from collections import defaultdict

# 使用 lambda 函数
dd_lambda = defaultdict(lambda: "N/A")
dd_lambda['name'] = 'Alice'
print(dd_lambda['name'])     # Alice
print(dd_lambda['age'])      # N/A

# 使用自定义函数
def default_list_with_zero():
    return [0]

dd_custom = defaultdict(default_list_with_zero)
dd_custom['scores'].append(95)
dd_custom['scores'].append(87)
print("自定义默认值:", dict(dd_custom))  # {'scores': [0, 95, 87]}
print("新键的默认值:", dd_custom['new_key'])  # [0]

OrderedDict - 有序字典

OrderedDict 基本用法

OrderedDict 是字典的子类,它记住了元素插入的顺序。在 Python 3.7+ 中,普通字典也保持插入顺序,但 OrderedDict 提供了额外的功能。

from collections import OrderedDict

# 创建有序字典
od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3

print("有序字典:", od)
print("键的顺序:", list(od.keys()))

# 从元组列表创建
items = [('name', 'Alice'), ('age', 25), ('city', 'New York')]
od2 = OrderedDict(items)
print("从列表创建:", od2)

OrderedDict 特有方法

from collections import OrderedDict

od = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

# popitem() - 默认删除最后一个项目
last_item = od.popitem()
print("删除的最后项目:", last_item)  # ('d', 4)
print("剩余项目:", od)

# popitem(last=False) - 删除第一个项目
od['d'] = 4  # 重新添加
first_item = od.popitem(last=False)
print("删除的第一项目:", first_item)  # ('a', 1)
print("剩余项目:", od)

# move_to_end() - 移动到末尾
od.move_to_end('b')
print("移动b到末尾:", od)

# move_to_end(last=False) - 移动到开头
od.move_to_end('c', last=False)
print("移动c到开头:", od)

OrderedDict 与普通字典的比较

from collections import OrderedDict

# 普通字典
regular_dict1 = {'a': 1, 'b': 2}
regular_dict2 = {'b': 2, 'a': 1}
print("普通字典相等性:", regular_dict1 == regular_dict2)  # True

# 有序字典
ordered_dict1 = OrderedDict([('a', 1), ('b', 2)])
ordered_dict2 = OrderedDict([('b', 2), ('a', 1)])
print("有序字典相等性:", ordered_dict1 == ordered_dict2)  # False

# 有序字典在相等性比较时考虑顺序
ordered_dict3 = OrderedDict([('a', 1), ('b', 2)])
print("相同顺序的有序字典:", ordered_dict1 == ordered_dict3)  # True

namedtuple - 命名元组

namedtuple 基本概念

namedtuple 是一个工厂函数,用于创建具有命名字段的tuple子类。它创建的类型既有元组的不可变性,又有通过名称访问字段的便利性。

from collections import namedtuple

# 定义一个命名元组类型
Person = namedtuple('Person', ['name', 'age', 'city'])

# 创建实例
person1 = Person('Alice', 30, 'New York')
person2 = Person(name='Bob', age=25, city='London')

print("person1:", person1)
print("通过索引访问:", person1[0])  # Alice
print("通过名称访问:", person1.name)  # Alice
print("通过属性访问年龄:", person1.age)  # 30

namedtuple 的字段定义方式

from collections import namedtuple

# 方式1:使用列表
Point1 = namedtuple('Point', ['x', 'y'])

# 方式2:使用字符串(空格分隔)
Point2 = namedtuple('Point', 'x y')

# 方式3:使用字符串(逗号分隔)
Point3 = namedtuple('Point', 'x,y')

# 所有方式创建的类型都相同
p1 = Point1(1, 2)
p2 = Point2(3, 4)
p3 = Point3(5, 6)

print("点1:", p1)
print("点2:", p2)
print("点3:", p3)

namedtuple 的常用方法

from collections import namedtuple

# 定义一个学生类型
Student = namedtuple('Student', 'name age grade')
student = Student('Alice', 20, 'A')

# _asdict() - 转换为字典
student_dict = student._asdict()
print("转为字典:", student_dict)  # {'name': 'Alice', 'age': 20, 'grade': 'A'}

# _replace() - 创建新实例并替换指定字段
new_student = student._replace(age=21, grade='A+')
print("替换后:", new_student)  # Student(name='Alice', age=21, grade='A+')
print("原对象:", student)  # Student(name='Alice', age=20, grade='A')

# _fields - 查看字段名
print("字段名:", Student._fields)  # ('name', 'age', 'grade')

# _make() - 从可迭代对象创建实例
data = ['Bob', 19, 'B']
student2 = Student._make(data)
print("从列表创建:", student2)  # Student(name='Bob', age=19, grade='B')

deque - 双端队列

deque 基本概念

deque(双端队列)是一个类似列表的容器,支持在两端快速添加和删除元素。它比列表在头部操作时更高效。

from collections import deque

# 创建双端队列
dq = deque([1, 2, 3, 4, 5])
print("初始队列:", dq)

# 右端添加和删除
dq.append(6)  # 右端添加
print("右端添加6:", dq)

right_item = dq.pop()  # 右端删除
print("右端删除的元素:", right_item)
print("删除后:", dq)

# 左端添加和删除
dq.appendleft(0)  # 左端添加
print("左端添加0:", dq)

left_item = dq.popleft()  # 左端删除
print("左端删除的元素:", left_item)
print("删除后:", dq)

deque 的限制长度

from collections import deque

# 创建限制长度的双端队列
limited_dq = deque(maxlen=3)

# 添加元素
for i in range(5):
    limited_dq.append(i)
    print(f"添加{i}后:", limited_dq)

# 输出:
# 添加0后: deque([0], maxlen=3)
# 添加1后: deque([0, 1], maxlen=3)
# 添加2后: deque([0, 1, 2], maxlen=3)
# 添加3后: deque([1, 2, 3], maxlen=3)  # 0被自动移除
# 添加4后: deque([2, 3, 4], maxlen=3)  # 1被自动移除

deque 的其他方法

from collections import deque

dq = deque([1, 2, 3, 4, 5])

# extend() 和 extendleft()
dq.extend([6, 7])
print("右端扩展:", dq)  # deque([1, 2, 3, 4, 5, 6, 7])

dq.extendleft([0, -1])
print("左端扩展:", dq)  # deque([-1, 0, 1, 2, 3, 4, 5, 6, 7])

# rotate() - 旋转队列
dq_rotate = deque([1, 2, 3, 4, 5])
dq_rotate.rotate(2)  # 向右旋转2位
print("向右旋转2位:", dq_rotate)  # deque([4, 5, 1, 2, 3])

dq_rotate.rotate(-1)  # 向左旋转1位
print("向左旋转1位:", dq_rotate)  # deque([5, 1, 2, 3, 4])

# clear() - 清空队列
dq.clear()
print("清空后:", dq)  # deque([])

# copy() - 浅拷贝
original = deque([1, 2, 3])
copied = original.copy()
print("原队列:", original)
print("拷贝队列:", copied)

ChainMap - 链式映射

ChainMap 基本概念

ChainMap 将多个字典或映射组合成一个单一的、可更新的视图。它不会合并字典,而是创建一个查找链,按顺序搜索底层映射。

from collections import ChainMap

# 创建多个字典
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict3 = {'c': 5, 'd': 6}

# 创建链式映射
chain = ChainMap(dict1, dict2, dict3)
print("链式映射:", chain)
print("所有键:", list(chain.keys()))
print("所有值:", list(chain.values()))

# 查找值(按顺序查找,返回第一个找到的值)
print("键'a'的值:", chain['a'])  # 1 (来自dict1)
print("键'b'的值:", chain['b'])  # 2 (来自dict1,虽然dict2也有'b')
print("键'c'的值:", chain['c'])  # 4 (来自dict2,虽然dict3也有'c')
print("键'd'的值:", chain['d'])  # 6 (来自dict3)

ChainMap 的修改操作

from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
chain = ChainMap(dict1, dict2)

print("修改前:")
print("dict1:", dict1)
print("dict2:", dict2)
print("chain:", chain)

# 修改操作只影响第一个字典
chain['a'] = 10  # 修改dict1中的'a'
chain['e'] = 5   # 在dict1中添加新键'e'

print("\n修改后:")
print("dict1:", dict1)  # {'a': 10, 'b': 2, 'e': 5}
print("dict2:", dict2)  # {'c': 3, 'd': 4} - 未改变
print("chain:", chain)

# 删除操作
del chain['b']  # 从dict1中删除'b'
print("\n删除'b'后:")
print("dict1:", dict1)  # {'a': 10, 'e': 5}

ChainMap 的方法

from collections import ChainMap

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
dict3 = {'e': 5, 'f': 6}

chain = ChainMap(dict1, dict2)

# maps - 获取所有映射的列表
print("所有映射:", chain.maps)  # [{'a': 1, 'b': 2}, {'c': 3, 'd': 4}]

# new_child() - 在前面添加新的空字典
new_chain = chain.new_child()
print("添加空字典后:", new_chain.maps)  # [{}, {'a': 1, 'b': 2}, {'c': 3, 'd': 4}]

# new_child(dict) - 在前面添加指定字典
new_chain2 = chain.new_child(dict3)
print("添加dict3后:", new_chain2.maps)  # [{'e': 5, 'f': 6}, {'a': 1, 'b': 2}, {'c': 3, 'd': 4}]

# parents - 获取除第一个映射外的新ChainMap
parent_chain = new_chain2.parents
print("父链映射:", parent_chain.maps)  # [{'a': 1, 'b': 2}, {'c': 3, 'd': 4}]

ChainMap 实际应用

from collections import ChainMap
import os

# 模拟配置优先级:命令行参数 > 环境变量 > 默认配置
def get_config():
    # 默认配置
    defaults = {
        'host': 'localhost',
        'port': 8080,
        'debug': False,
        'timeout': 30
    }
    
    # 环境变量配置
    env_config = {
        'host': os.environ.get('HOST', ''),
        'port': int(os.environ.get('PORT', 0)) if os.environ.get('PORT') else 0,
        'debug': os.environ.get('DEBUG', '').lower() == 'true'
    }
    # 移除空值
    env_config = {k: v for k, v in env_config.items() if v}
    
    # 命令行参数配置(模拟)
    cmd_config = {
        'debug': True,
        'port': 3000
    }
    
    # 创建配置链(优先级从高到低)
    config = ChainMap(cmd_config, env_config, defaults)
    return config

# 获取最终配置
final_config = get_config()
print("最终配置:")
for key, value in final_config.items():
    print(f"  {key}: {value}")

用户自定义容器类

UserDict - 字典包装类

UserDict 提供了一个字典的包装类,便于创建自定义字典类型。

from collections import UserDict

class CaseInsensitiveDict(UserDict):
    """不区分大小写的字典"""
    
    def __setitem__(self, key, value):
        # 将键转换为小写存储
        super().__setitem__(key.lower(), value)
    
    def __getitem__(self, key):
        # 查找时也转换为小写
        return super().__getitem__(key.lower())
    
    def __contains__(self, key):
        # 检查是否包含键时也转换为小写
        return super().__contains__(key.lower())
    
    def __delitem__(self, key):
        # 删除时也转换为小写
        super().__delitem__(key.lower())

# 测试不区分大小写的字典
ci_dict = CaseInsensitiveDict()
ci_dict['Name'] = 'Alice'
ci_dict['AGE'] = 30
ci_dict['City'] = 'New York'

print("不区分大小写字典:", ci_dict.data)  # {'name': 'Alice', 'age': 30, 'city': 'New York'}
print("访问'name':", ci_dict['name'])     # Alice
print("访问'NAME':", ci_dict['NAME'])     # Alice
print("访问'Name':", ci_dict['Name'])     # Alice
print("'AGE' in dict:", 'AGE' in ci_dict)  # True
print("'age' in dict:", 'age' in ci_dict)  # True

UserList - 列表包装类

from collections import UserList

class NumberList(UserList):
    """只允许数字的列表"""
    
    def append(self, item):
        if not isinstance(item, (int, float)):
            raise TypeError("只允许添加数字")
        super().append(item)
    
    def insert(self, index, item):
        if not isinstance(item, (int, float)):
            raise TypeError("只允许插入数字")
        super().insert(index, item)
    
    def extend(self, items):
        for item in items:
            if not isinstance(item, (int, float)):
                raise TypeError("只允许添加数字")
        super().extend(items)
    
    def sum(self):
        """计算列表中所有数字的和"""
        return sum(self.data)
    
    def average(self):
        """计算平均值"""
        if not self.data:
            return 0
        return sum(self.data) / len(self.data)

# 测试数字列表
num_list = NumberList([1, 2, 3])
num_list.append(4)
num_list.extend([5, 6])

print("数字列表:", num_list.data)  # [1, 2, 3, 4, 5, 6]
print("总和:", num_list.sum())     # 21
print("平均值:", num_list.average())  # 3.5

UserString - 字符串包装类

from collections import UserString

class ReversibleString(UserString):
    """可反转的字符串类"""
    
    def reverse(self):
        """反转字符串"""
        return ReversibleString(self.data[::-1])
    
    def is_palindrome(self):
        """检查是否是回文,正读倒着读都一样"""
        clean_data = self.data.lower().replace(' ', '')
        return clean_data == clean_data[::-1]
    
    def count_vowels(self):
        """计算元音字母数量"""
        vowels = 'aeiouAEIOU'
        return sum(1 for char in self.data if char in vowels)

# 测试可反转字符串
rev_str = ReversibleString("Hello World")
print("原字符串:", rev_str)  # Hello World
print("反转字符串:", rev_str.reverse())  # dlroW olleH
print("是否是回文:", rev_str.is_palindrome())  # False
print("元音字母数量:", rev_str.count_vowels())  # 3

# 测试回文
palindrome = ReversibleString("A man a plan a canal Panama")
print("回文测试:", palindrome.is_palindrome())  # True

性能对比和最佳实践

性能对比

import time
from collections import defaultdict, deque

def performance_comparison():
    """比较不同数据结构的性能"""
    
    # 列表 vs deque 在头部插入的性能对比
    print("=== 列表 vs deque 头部插入性能对比 ===")
    
    # 测试列表头部插入
    start_time = time.time()
    test_list = []
    for i in range(10000):
        test_list.insert(0, i)
    list_time = time.time() - start_time
    
    # 测试deque头部插入
    start_time = time.time()
    test_deque = deque()
    for i in range(10000):
        test_deque.appendleft(i)
    deque_time = time.time() - start_time
    
    print(f"列表头部插入10000次: {list_time:.4f}秒")
    print(f"deque头部插入10000次: {deque_time:.4f}秒")
    print(f"deque比列表快 {list_time/deque_time:.1f} 倍")
    
    # 普通字典 vs defaultdict 性能对比
    print("\n=== 普通字典 vs defaultdict 性能对比 ===")
    
    # 测试普通字典
    start_time = time.time()
    normal_dict = {}
    for i in range(10000):
        key = f"key_{i % 100}"
        if key not in normal_dict:
            normal_dict[key] = []
        normal_dict[key].append(i)
    normal_dict_time = time.time() - start_time
    
    # 测试defaultdict
    start_time = time.time()
    default_dict = defaultdict(list)
    for i in range(10000):
        key = f"key_{i % 100}"
        default_dict[key].append(i)
    default_dict_time = time.time() - start_time
    
    print(f"普通字典分组: {normal_dict_time:.4f}秒")
    print(f"defaultdict分组: {default_dict_time:.4f}秒")
    print(f"defaultdict比普通字典快 {normal_dict_time/default_dict_time:.1f} 倍")

performance_comparison()

最佳实践建议

from collections import Counter, defaultdict, deque, namedtuple

def best_practices_examples():
    """Collections模块最佳实践示例"""
    
    print("=== Collections 最佳实践 ===")
    
    # 1. 使用Counter进行频率统计
    print("1. 使用Counter进行统计:")
    words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
    
    # 好的做法
    word_count = Counter(words)
    print("  最常见的词:", word_count.most_common(2))
    
    # 2. 使用defaultdict避免KeyError
    print("\n2. 使用defaultdict分组数据:")
    students = [("Alice", "Math"), ("Bob", "English"), ("Alice", "Science")]
    
    # 好的做法
    groups = defaultdict(list)
    for name, subject in students:
        groups[name].append(subject)
    print("  学生课程:", dict(groups))
    
    # 3. 使用deque实现高效队列
    print("\n3. 使用deque实现队列:")
    queue = deque()
    
    # 添加任务
    for task in ["task1", "task2", "task3"]:
        queue.append(task)
    
    # 处理任务
    while queue:
        current_task = queue.popleft()
        print(f"  处理任务: {current_task}")
    
    # 4. 使用namedtuple创建轻量级类
    print("\n4. 使用namedtuple创建数据结构:")
    Point = namedtuple('Point', 'x y')
    
    points = [Point(1, 2), Point(3, 4), Point(5, 6)]
    total_x = sum(p.x for p in points)
    total_y = sum(p.y for p in points)
    print(f"  点的总和: x={total_x}, y={total_y}")
    
    # 5. 合理选择数据结构
    print("\n5. 数据结构选择建议:")
    recommendations = {
        "频率统计": "使用 Counter",
        "分组数据": "使用 defaultdict",
        "队列操作": "使用 deque",
        "轻量级数据类": "使用 namedtuple",
        "保持插入顺序": "使用 OrderedDict (Python < 3.7)",
        "链式查找": "使用 ChainMap",
        "自定义容器": "使用 UserDict/UserList/UserString"
    }
    
    for use_case, recommendation in recommendations.items():
        print(f"  {use_case}: {recommendation}")

best_practices_examples()

总结

collections 模块为Python提供了强大的容器数据类型,每种类型都有其特定的使用场景:

  • Counter: 适用于计数和频率统计
  • defaultdict: 适用于避免KeyError和数据分组
  • OrderedDict: 适用于需要保持插入顺序的场景(Python 3.7+中普通字典已保持顺序)
  • namedtuple: 适用于创建轻量级、不可变的数据结构
  • deque: 适用于需要高效双端操作的队列场景
  • ChainMap: 适用于多层配置和变量查找
  • UserDict/UserList/UserString: 适用于创建自定义容器类型