5、python 模块与包

Python 模块与包

本章学习知识点

  • 模块导入:import 模块、from 模块 import 函数、自定义模块
  • 常用标准库:
    • os/sys:系统操作(文件路径、环境变量)
    • datetime:时间处理(日期转换、计时)
    • json:JSON 数据解析与生成
    • collections:高级数据结构(defaultdict、deque、Counter)

模块与包是 Python 代码模块化、可维护性的核心,能让你实现「代码复用、功能拆分、第三方集成」。本手册聚焦 模块导入机制、自定义模块与包 ,并深入讲解高频实用的标准库(os/sys/datetime/json/collections

一、模块与包基础

1.1 核心概念

  • 模块(Module) :一个 .py 文件就是一个模块,包含变量、函数、类等代码,可被其他文件导入使用(核心价值:代码复用)。

  • 包(Package):包含:__init__.py文件的文件夹,用于组织多个相关模块(核心价值:按功能拆分代码,避免模块名冲突)。

    • 注意:Python 3.3+ 支持「命名空间包」(无需 __init__.py),但推荐保留 __init__.py 用于初始化包、控制导入行为。

1.2 模块导入机制

  • Python 导入模块时,会按以下顺序查找模块文件:

    1. 当前执行脚本所在目录(优先);
    2. 系统环境变量 PYTHONPATH 指定的目录;
    3. Python 安装目录下的标准库目录;
    4. 第三方库目录(如 site-packages)。
  • 导入模块的 4 种常用方式,需根据场景灵活选择:

    • import 模块名(导入整个模块)

      • 语法:import 模块名 [as 别名]

      • 特点:导入整个模块,通过「模块名。属性」访问模块内的函数 / 变量 / 类,避免命名冲突。

      • 适用场景:模块功能较多,需多次使用模块内的不同属性。

        python 复制代码
        # 示例1:导入标准库模块(无别名)
        import math
        print(math.pi)  # 访问模块变量 → 3.141592653589793
        print(math.sqrt(16))  # 调用模块函数 → 4.0    计算平方根比如25返回就是5
        
        # 示例2:导入模块并指定别名(简化书写)
        import datetime as dt
        current_time = dt.datetime.now()
        print(current_time)  # 输出当前时间 → 2025-11-27 10:00:00.123456
    • from 模块名 import 属性(导入模块指定属性)

      • 语法:from 模块名 import 函数/变量/类 [as 别名]

      • 特点:直接导入模块内的指定属性,可直接使用(无需加模块名前缀),代码更简洁。

      • 适用场景:仅需使用模块内的少数几个属性。

        python 复制代码
        # 示例1:导入模块的单个属性
        from math import pi, sqrt
        print(pi)  # 直接使用变量 → 3.141592653589793
        print(sqrt(25))  # 直接调用函数 → 5.0
        
        # 示例2:导入属性并指定别名(避免命名冲突)
        from math import pow as power
        print(power(2, 3))  # 调用别名函数 → 8.0
        
        # 示例3:导入模块的所有属性(不推荐,易冲突)
        from math import *
        print(sin(pi/2))  # 直接使用所有属性 → 1.0
        # 警告:若当前文件有同名函数/变量,会被覆盖
    • from 包名 import 模块(导入包内模块)

      • 语法:from 包名 import 模块名 [as 别名]from 包名.模块名 import 属性

      • 特点:按包的层级结构导入模块,适合大型项目的代码组织。

        python 复制代码
        # 假设项目结构如下:
        # my_project/
        #   main.py
        #   utils/  # 包
        #     __init__.py
        #     file_ops.py  # 模块(含 read_file 函数)
        #     str_ops.py   # 模块(含 format_str 函数)
        
        # 在 main.py 中导入包内模块
        from utils import file_ops
        content = file_ops.read_file("test.txt")  # 包.模块.函数
        
        # 直接导入包内模块的属性
        from utils.str_ops import format_str
        formatted = format_str("hello")  # 直接使用函数
        
        # 导入包内模块并指定别名
        from utils import file_ops as fo
        content = fo.read_file("test.txt")
    • import 包名.模块名(完整路径导入)

      • 语法:import 包名.模块名 [as 别名]

      • 特点:明确指定模块的完整路径,可读性强,避免同名模块冲突。

        python 复制代码
        # 延续上面的项目结构
        import utils.file_ops
        content = utils.file_ops.read_file("test.txt")
        
        # 完整路径导入并指定别名
        import utils.str_ops as so
        formatted = so.format_str("hello")

1.3 自定义模块

自定义模块就是创建 .py 文件,编写代码后供其他文件导入,步骤如下:

  1. 创建模块文件(my_module.py

    python 复制代码
    # my_module.py(自定义模块)
    # 模块变量
    MODULE_NAME = "我的自定义模块"
    VERSION = "1.0"
    
    # 模块函数
    def add(a, b):
        """两数相加"""
        return a + b
    
    def multiply(a, b):
        """两数相乘"""
        return a * b
    
    # 模块类
    class Calculator:
        def subtract(self, a, b):
            """两数相减"""
            return a - b
  2. 导入自定义模块(在同一目录下的 main.py

    python 复制代码
    # main.py
    # 方式1:导入整个模块
    import my_module
    print(my_module.MODULE_NAME)  # 访问变量 → 我的自定义模块
    print(my_module.add(3, 5))  # 调用函数 → 8
    calc = my_module.Calculator()
    print(calc.subtract(10, 4))  # 调用类方法 → 6
    
    # 方式2:导入模块指定属性
    from my_module import multiply, Calculator
    print(multiply(4, 5))  # → 20
    calc2 = Calculator()
    print(calc2.subtract(20, 7))  # → 13
    
    # 方式3:导入模块并指定别名
    import my_module as mm
    print(mm.VERSION)  # → 1.0
  3. 注意事项:

    • 模块名遵循 PEP8 规范(小写 + 下划线,如 my_module),避免与标准库 / 第三方库模块名冲突(如不要命名为 os.pyjson.py);

    • 若自定义模块不在当前目录,需将模块所在目录添加到 sys.path(见下文 sys 库);

    • 模块被导入时,其顶层代码(如变量定义、函数定义、类定义)会被执行一次(可通过 if __name__ == "__main__": 控制仅在直接运行模块时执行的代码)。

      python 复制代码
      # 优化 my_module.py:添加主程序入口
      if __name__ == "__main__":
          # 仅当直接运行 my_module.py 时执行(导入时不执行)
          print("直接运行自定义模块")
          print(add(2, 3))  # 测试函数

1.4 自定义包

自定义包用于组织多个相关模块,步骤如下:

  1. 创建包结构

    python 复制代码
    # 项目结构
    my_project/
      main.py
      tools/  # 包名
        __init__.py  # 包初始化文件(可空,也可用于导入配置)
        math_ops.py  # 模块1:数学运算
        str_ops.py   # 模块2:字符串处理
  2. 编写包内模块代码

    python 复制代码
    # tools/math_ops.py
    def calculate_sum(n):
        """计算1到n的和"""
        return sum(range(1, n+1))
    
    # tools/str_ops.py
    def reverse_str(s):
        """反转字符串"""
        return s[::-1]
  3. (可选)优化 __init__.py(控制包导入行为)

    • __init__.py 可用于:1. 初始化包变量;2. 简化导入路径(通过 __all__ 指定默认导入的模块 / 属性)。

      python 复制代码
      # tools/__init__.py
      # 包变量
      PACKAGE_DESCRIPTION = "工具包:包含数学运算和字符串处理功能"
      
      # 指定 from tools import * 时导入的模块(可选)
      __all__ = ["math_ops", "str_ops"]
      
      # 简化导入:让用户可直接 from tools import calculate_sum
      from .math_ops import calculate_sum
      from .str_ops import reverse_str
  4. 导入包并使用

    python 复制代码
    # main.py
    # 方式1:导入包内模块
    from tools import math_ops, str_ops
    print(math_ops.calculate_sum(10))  # → 55
    print(str_ops.reverse_str("hello"))  # → olleh
    
    # 方式2:通过 __init__.py 简化导入(直接导入属性)
    from tools import calculate_sum, reverse_str
    print(calculate_sum(5))  # → 15
    print(reverse_str("python"))  # → nohtyp
    
    # 方式3:导入整个包
    import tools
    print(tools.PACKAGE_DESCRIPTION)  # 访问包变量
    print(tools.calculate_sum(8))  # 调用简化导入的函数

二、常用标准库实战

Python 标准库是内置的「工具集」,无需安装即可使用,以下是开发中最常用的 5 个标准库,覆盖系统操作、时间处理、数据解析、高级数据结构等核心场景。

模块大类 子模块 / 核心功能 核心用途 关键特性 / 适用场景
时间处理模块 datetime 时间运算(增减)、时间字段修改、高精度时间获取 比 time 更直观,支持 timedelta 时间差计算
文件与目录操作模块 os 目录增删、路径处理、系统环境获取、文件属性查看 跨系统路径兼容,基础文件 / 目录管理
系统交互模块 sys 获取 Python 解释器信息、命令行参数处理、程序退出控制 命令行工具开发、系统参数获取
数据序列化模块 json 跨语言数据序列化 / 反序列化,支持基础数据类型 多语言交互、接口数据传输
数据结构扩展模块 collections 提供高性能专用数据结构(Counter、defaultdict、OrderedDict、deque 等) 弥补内置数据结构不足,提升开发效率

2.1、sys 库

sys 库用于与 Python 解释器本身交互,核心功能包括「命令行参数获取、系统路径配置、退出程序」等。

  • 核心功能与方法

    方法 / 属性 功能描述 示例
    sys.argv 获取命令行参数(列表,第一个元素是脚本名) 运行 python main.py 10 20sys.argv → ["main.py", "10", "20"]
    sys.path Python 模块搜索路径列表(可动态添加) print(sys.path) → [当前目录, 标准库目录, ...]
    sys.version 获取 Python 版本信息 print(sys.version) → 3.9.7 (default, ...)
    sys.platform 获取操作系统平台 print(sys.platform) → linux/win32/darwin(Mac)
    sys.exit(code=0) 退出程序(code=0 表示正常退出,非 0 表示异常) sys.exit(1)(异常退出)
    sys.stdin 标准输入流(如读取用户输入) line = sys.stdin.readline()
    sys.stdout 标准输出流(如打印内容) sys.stdout.write("Hello")
  • 示例

    python 复制代码
    import sys
    
    # 1. 解析命令行参数(实现简易计算器)
    print("命令行参数:", sys.argv)
    if len(sys.argv) != 4:
        print("用法:python main.py <操作符> <数字1> <数字2>")
        print("示例:python main.py add 10 20")
        sys.exit(1)  # 异常退出(非 0 状态码)
    
    op = sys.argv[1]
    num1 = float(sys.argv[2])
    num2 = float(sys.argv[3])
    
    if op == "add":
        print(f"{num1} + {num2} = {num1 + num2}")
    elif op == "sub":
        print(f"{num1} - {num2} = {num1 - num2}")
    elif op == "mul":
        print(f"{num1} × {num2} = {num1 * num2}")
    elif op == "div":
        if num2 == 0:
            print("错误:除数不能为 0")
            sys.exit(1)
        print(f"{num1} ÷ {num2} = {num1 / num2:.2f}")
    else:
        print("支持的操作符:add/sub/mul/div")
        sys.exit(1)
    
    ############ 写法2 ############
    import sys
    print(sys.argv)
    
    if len(sys.argv) != 4 or len(sys.argv) >= 5:
        print("用法:python main.py <操作符[add,sub,mul]> <数字1> <数字2>")
        print("示例:python main.py add 10 20")
        sys.exit(1)  # 异常退出(非 0 状态码)
    
    # 如果不去掉第1个 控制台四个参数 打印['绝对路径/名称.py', 'add', '10', '20']
    operator = sys.argv[1::]
    
    def args(*args):
        num1=float(args[1])
        num2=float(args[2])
        if args[0]=='add':
            print(num1+num2)
        elif args[0]=='sub':
            print(num1-num2)
        elif args[0]=='mul':
            print(num1*num2)
        else:
            print("请输入正确的操作符")
            sys.exit(1)
    
    args(*operator)
        
    # 2. 动态添加模块搜索路径(导入非当前目录的自定义模块)
    # 假设自定义模块在 /home/user/my_modules 目录下
    custom_module_dir = "/home/user/my_modules"
    if custom_module_dir not in sys.path:
        sys.path.append(custom_module_dir)  # 添加到搜索路径
        print(f"已添加模块路径:{custom_module_dir}")
    
    # 之后即可导入该目录下的模块
    # import my_custom_module

2.1、os 库

os 库提供与操作系统交互的接口,核心用于「文件路径操作、目录管理、系统命令执行」,是文件处理的基础。

  • 核心功能与方法

    方法 / 属性 功能描述 示例
    os.getcwd() 获取当前工作目录 print(os.getcwd()) → /home/user/project
    os.chdir(path) 切换工作目录 os.chdir("/home/user")
    os.listdir(path=".") 列出目录下的所有文件 / 子目录 os.listdir("./tools") → ["math_ops.py", ...]
    os.mkdir(path) 创建单个目录(父目录必须存在) os.mkdir("./data")
    os.makedirs(path) 递归创建目录(父目录不存在则自动创建) os.makedirs("./data/logs")
    os.remove(path) 删除文件 os.remove("./test.txt")
    os.rmdir(path) 删除空目录 os.rmdir("./data")
    os.removedirs(path) 递归删除空目录 os.removedirs("./data/logs")
    os.rename(old, new) 重命名文件 / 目录 os.rename("old.txt", "new.txt")
    os.path.exists(path) 判断路径是否存在(文件 / 目录) os.path.exists("./tools") → True
    os.path.isfile(path) 判断是否为文件 os.path.isfile("./main.py") → True
    os.path.isdir(path) 判断是否为目录 os.path.isdir("./tools") → True
    os.path.join(p1, p2, ...) 拼接路径(自动适配系统分隔符) os.path.join("./data", "logs") → ./data/logs
    os.path.abspath(path) 获取路径的绝对路径 os.path.abspath("./main.py") → /home/user/project/main.py
    os.path.splitext(path) 分割文件名和扩展名 os.path.splitext("test.txt") → ("test", ".txt")
  • 示例

    python 复制代码
    import os
    
    # 1. 路径拼接(跨平台兼容)
    # 错误方式:直接用 + 拼接(Windows 用 \,Linux 用 /,易出错)
    # bad_path = "./data" + "/logs"
    # 正确方式:用 os.path.join
    data_dir = os.path.join(os.getcwd(), "data")
    log_dir = os.path.join(data_dir, "logs")
    print("日志目录路径:", log_dir)
    
    # 2. 目录创建(不存在则创建)
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)  # 递归创建
        print(f"目录 {log_dir} 创建成功")
    else:
        print(f"目录 {log_dir} 已存在")
    
    # 3. 遍历目录下的所有 .py 文件
    project_dir = os.getcwd()
    py_files = []
    for root, dirs, files in os.walk(project_dir):  # 递归遍历目录
        for file in files:
            if file.endswith(".py"):  # 筛选 .py 文件
                file_path = os.path.join(root, file)
                py_files.append(file_path)
    
    print(f"\n项目中所有 .py 文件(共 {len(py_files)} 个):")
    for path in py_files:
        print(path)
    
    # 4. 文件重命名(批量修改后缀)        
    current_path=os.path.dirname(os.path.abspath(__file__))
    file_list=["file1.txt","file2.txt","file3.txt"]
    for file in file_list:
        get_check_file_path=os.path.join(current_path,"bac") # 多用os.getpwd()看一下当前路径
        name,type=os.path.splitext(file)      # "filex",".txt"
        newe_nam=name+".md"   # 改为 .md 后缀
        os.rename(os.path.join(get_check_file_path,file), os.path.join(get_check_file_path,newe_nam))
        print(f"旧文件:{file} --> 新文件:{os.path.join(get_check_file_path,newe_nam)}")
  • 获取「脚本文件所在目录」

    python 复制代码
    # 当前在【D:\docker\miscellaneous\python\模块与包\模块使用】下使用os.getcwd(),查看只到  D:\docker\miscellaneous\python 怎么获取到 D:\docker\miscellaneous\python\模块与包\模块使用
    # 
    # 获取当前脚本文件的绝对路径
    script_path = os.path.abspath(__file__)
    # 获取脚本文件所在的目录(即模块使用目录)
    script_dir = os.path.dirname(script_path)
    
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    print(os.getcwd())

2.3、datetime 库

datetime 库是 Python 处理时间的核心库,支持「日期转换、时间计算、格式化输出」,比 time 库更强大、易用。

  • 核心功能与方法

    类 / 方法 功能描述 示例
    datetime.datetime.now() 获取当前本地时间(datetime 对象) dt.now() → 2025-11-27 10:30:00.123456
    datetime.date.today() 获取当前日期(date 对象) date.today() → 2025-11-27
    datetime.datetime.strptime(date_str, format) 字符串转 datetime 对象 strptime("2025-11-27", "%Y-%m-%d") → 2025-11-27 00:00:00
    datetime.datetime.strftime(format) datetime 对象转字符串 dt.strftime("%Y-%m-%d %H:%M:%S") → "2025-11-27 10:30:00"
    datetime.timedelta(days/hours/minutes/seconds) 时间间隔对象(用于时间计算) timedelta(days=1) → 1 天
    dt1 - dt2 两个 datetime 对象相减,返回 timedelta dt.now() - dt(2025,1,1) → 330 天...
    dt + timedelta datetime 对象加时间间隔,返回新 datetime dt.now() + timedelta(hours=3) → 3 小时后
    timestamp 将时间转换为时间戳
    fromtimestamp 将时间戳转换为时间
    时间格式化符号(常用):
    • %Y:4 位年份(如 2025)
    • %m:2 位月份(01-12)
    • %d:2 位日期(01-31)
    • %H:24 小时制(00-23)
    • %M:分钟(00-59)
    • %S:秒(00-59)
    • %w:星期(0-6,0 是周日)
  • 示例

    python 复制代码
    from datetime import datetime, date, timedelta
    
    # 1. 获取当前时间与日期
    current_dt = datetime.now()  # 含时分秒毫秒
    current_date = date.today()  # 仅日期
    print("当前完整时间:", current_dt)
    print("当前日期:", current_date)
    print("当前小时:", current_dt.hour)
    print("当前星期:", current_dt.weekday())  # 0=周一,6=周日
    
    # 2. 时间格式化(datetime → 字符串)
    formatted_dt = current_dt.strftime("%Y-%m-%d %H:%M:%S")
    print("格式化时间:", formatted_dt)  # → 2025-11-27 10:35:22
    
    # 3. 字符串转时间(字符串 → datetime)
    time_str = "2025-12-01 12:00:00"
    parsed_dt = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
    print("解析后的时间:", parsed_dt)
    print("解析后的年份:", parsed_dt.year)
    
    # 4. 时间计算(加减时间间隔)
    # 3天后的时间
    three_days_later = current_dt + timedelta(days=3)
    print("3天后:", three_days_later.strftime("%Y-%m-%d"))
    
    # 2小时30分钟前的时间
    two_hours_ago = current_dt - timedelta(hours=2, minutes=30)
    print("2小时30分钟前:", two_hours_ago.strftime("%H:%M:%S"))
    
    # 计算两个日期的差值
    date1 = date(2025, 1, 1)
    date2 = date(2025, 11, 27)
    delta = date2 - date1
    print("两个日期相差:", delta.days, "天")
    
    # 5. 函数执行计时(统计代码运行时间)
    def test_func():
        sum(range(1, 1000000))
    
    start_time = datetime.now()
    test_func()
    end_time = datetime.now()
    duration = (end_time - start_time).total_seconds()  # 耗时(秒)
    print(f"函数执行耗时:{duration:.4f} 秒")
    
    # 时间戳转换
    print(dt.datetime.now().timestamp())  # 1764573102.217211
    print(dt.datetime.fromtimestamp(1764573102.217211))

2.4、json 库

JSON 是前后端交互、数据存储的常用格式,json 库支持「JSON 字符串 → Python 对象」(解析)和「Python 对象 → JSON 字符串」(生成)。

  • 核心映射关系(Python ↔ JSON)

    Python 类型 JSON 类型
    dict object(对象)
    list/tuple array(数组)
    str string(字符串)
    int/float number(数字)
    True true
    False false
    None null
  • 核心方法

    功能描述 方法 示例
    Python 对象 → JSON 字符串(indent 格式化输出) json.dumps(obj, ensure_ascii=False, indent=2) dumps({"name":"小明"}, ensure_ascii=False) → '{"name":"小明"}'
    JSON 字符串 → Python 对象 json.loads(json_str) loads('{"name":"小明"}') → {"name":"小明"}
    Python 对象 → JSON 文件(fp 是文件对象) json.dump(obj, fp, ensure_ascii=False, indent=2) dump(data, open("data.json", "w"))
    JSON 文件 → Python 对象 json.load(fp) load(open("data.json", "r")) → data
  • 实战

    python 复制代码
    import json
    
    # 1. Python 对象 → JSON 字符串(生成)
    data = {
        "name": "小明",
        "age": 20,
        "gender": "男",
        "is_student": True,
        "hobbies": ["篮球", "音乐", "旅行"],
        "address": {
            "city": "上海",
            "district": "浦东新区"
        },
        "score": None
    }
    
    # dumps 生成 JSON 字符串(ensure_ascii=False 保留中文,indent=2 格式化)
    json_str = json.dumps(data, ensure_ascii=False, indent=2)
    print("JSON 字符串:")
    print(json_str)
    
    # 2. JSON 字符串 → Python 对象(解析)
    parsed_data = json.loads(json_str)
    print("\n解析后的 Python 对象:")
    print(type(parsed_data))  # → <class 'dict'>
    print("姓名:", parsed_data["name"])
    print("爱好:", parsed_data["hobbies"][0])
    
    # 3. Python 对象 → JSON 文件(写入文件)
    current_dir= os.path.dirname(os.path.abspath(__file__))
    with open(f"{os.path.join(current_dir, 'data.json')}", 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    print(f"数据已保存到 {current_dir}")
    
    # 4. JSON 文件 → Python 对象(读取文件)
    with open(f"{os.path.join(current_dir, 'data.json')}", 'r', encoding='utf-8') as f:
        loaded_data = json.load(f)
    print("\n从文件读取的 JSON 数据:")
    print(loaded_data["address"]["city"])  # → 上海
    
    # 5. 处理复杂 JSON 数据(嵌套解析)
    complex_json = '''
    {
      "code": 200,
      "message": "success",
      "data": {
        "total": 2,
        "users": [
          {"id": 1, "name": "小红", "age": 18},
          {"id": 2, "name": "小李", "age": 19}
        ]
      }
    }
    '''
    result = json.loads(complex_json)
    if result["code"] == 200:
        users = result["data"]["users"]
        print("\n用户列表:")
        for user in users:
            print(f"ID:{user['id']},姓名:{user['name']}")

2.5、collections

collections 库提供了比内置数据结构(list/dict/set/tuple)更强大的扩展类型,解决了内置结构的诸多痛点(如字典缺省值、有序字典、计数统计等)。

  • 核心类型与用法

    1. defaultdict:带默认值的字典

      • 痛点:内置 dict 访问不存在的键会报错,需用 get(key, default) 或判断键是否存在;

      • 解决方案:defaultdict 初始化时指定「默认值类型 / 函数」,访问不存在的键时自动生成默认值。

        python 复制代码
        from collections import defaultdict
        
        # 示例1:默认值为列表(用于分组)
        student_scores = [
            ("小明", "数学", 90),
            ("小明", "英语", 85),
            ("小红", "数学", 95),
            ("小红", "英语", 88)
        ]
        
        # defaultdict(list):不存在的键默认生成空列表
        score_dict = defaultdict(list)
        for name, subject, score in student_scores:
            score_dict[name].append((subject, score))  # 无需判断 name 是否存在
        
        print("学生成绩分组:")
        for name, scores in score_dict.items():
            print(f"{name}:{scores}")
        # 输出:
        # 小明:[('数学', 90), ('英语', 85)]
        # 小红:[('数学', 95), ('英语', 88)]
        
        # 示例2:默认值为整数(用于计数)
        word_count = defaultdict(int)
        words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
        for word in words:
            # defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'cherry': 1})
            word_count[word] += 1  # 不存在的键默认值为 0,直接累加
        
        print("\n单词计数:", dict(word_count))  # → {'apple':3, 'banana':2, 'cherry':1}
      复制代码
  1. Counter:计数统计工具

    • 功能:专门用于「可迭代对象的元素计数」,比 defaultdict(int) 更简洁、功能更强。

      python 复制代码
      from collections import Counter
      
      # 示例1:字符串字符计数
      s = "abracadabra"
      char_count = Counter(s)
      print("字符计数:", char_count)  # → Counter({'a':5, 'b':2, 'r':2, 'c':1, 'd':1})
      
      # 示例2:列表元素计数
      nums = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
      num_count = Counter(nums)
      print("数字计数:", num_count)  # → Counter({4:4, 3:3, 2:2, 1:1})
      
      # 示例3:获取Top N元素(常用场景)
      top2 = num_count.most_common(2)  # 获取出现次数最多的2个元素
      print("出现次数最多的2个数字:", top2)  # → [(4,4), (3,3)]
      
      # 示例4:计数器运算(交集/并集/差集)
      c1 = Counter([1,2,3,3])
      c2 = Counter([3,4,4,5])
      print("交集(共同元素的最小计数):", c1 & c2)  # → Counter({3:1})
      print("并集(所有元素的最大计数):", c1 | c2)  # → Counter({1:1,2:1,3:2,4:2,5:1})
      print("差集(c1有但c2没有的元素):", c1 - c2)  # → Counter({1:1,2:1,3:1})
  2. deque:双端队列(高效增删)

    • 痛点:内置 list 是动态数组,头部增删元素(insert(0)/pop(0))效率低(时间复杂度 O (n));

    • 解决方案:deque 是双端队列,头部 / 尾部增删元素效率极高(时间复杂度 O (1)),支持最大长度限制。

      python 复制代码
      from collections import deque
      
      # 示例1:基础操作(头部/尾部增删)
      dq = deque([1, 2, 3])
      dq.append(4)  # 尾部添加 → deque([1,2,3,4])
      dq.appendleft(0)  # 头部添加 → deque([0,1,2,3,4])
      dq.pop()  # 尾部删除 → 4 → deque([0,1,2,3])
      dq.popleft()  # 头部删除 → 0 → deque([1,2,3])
      print("双端队列:", dq)
      
      # 示例2:最大长度限制(超出则自动删除另一端元素)
      limited_dq = deque(maxlen=3)  # 最大长度3
      limited_dq.append(1)
      limited_dq.append(2)
      limited_dq.append(3)
      print("限制长度3的队列:", limited_dq)  # → deque([1,2,3], maxlen=3)
      limited_dq.append(4)  # 超出长度,删除头部元素1
      print("添加4后:", limited_dq)  # → deque([2,3,4], maxlen=3)
      
      # 示例3:高效滑动窗口(常用场景)
      def sliding_window(nums, k):
          dq = deque()
          result = []
          for i, num in enumerate(nums):
              # 移除窗口外的元素(索引小于 i-k+1 的元素)
              while dq and dq[0] < i - k + 1:
                  dq.popleft()
              # 移除队列中比当前元素小的元素(维护队列递减)
              while dq and nums[dq[-1]] < num:
                  dq.pop()
              dq.append(i)
              # 窗口大小达到 k 后,记录最大值(队列头部)
              if i >= k - 1:
                  result.append(nums[dq[0]])
          return result
      
      nums = [1, 3, -1, -3, 5, 3, 6, 7]
      k = 3
      print("滑动窗口最大值:", sliding_window(nums, k))  # → [3,3,5,5,6,7]

三、综合实战

  • 日志收集工具(os+datetime+json+collections)

    python 复制代码
    """
    功能:收集指定目录下的日志文件,解析错误日志,统计错误类型并保存为 JSON 报告
    技术点:os 目录遍历、datetime 时间处理、json 数据生成、collections.Counter 计数
    """
    import os
    import json
    from datetime import datetime
    from collections import Counter
    
    def collect_error_logs(log_dir, output_file):
        # 1. 验证日志目录是否存在
        if not os.path.isdir(log_dir):
            print(f"错误:目录 {log_dir} 不存在")
            return
    
        # 2. 遍历目录下的所有 .log 文件
        error_records = []
        error_types = []
        for root, dirs, files in os.walk(log_dir):
            for file in files:
                if file.endswith(".log"):
                    log_file_path = os.path.join(root, file)
                    print(f"正在解析日志文件:{log_file_path}")
    
                    # 3. 读取日志文件,筛选错误日志(假设错误日志以 ERROR: 开头)
                    with open(log_file_path, "r", encoding="utf-8", errors="ignore") as f:
                        for line_num, line in enumerate(f, start=1):
                            line = line.strip()
                            if line.startswith("ERROR:"):
                                # 解析日志时间(假设日志格式:[2025-11-27 10:00:00] ERROR: ...)
                                if "[" in line and "]" in line:
                                    time_str = line.split("]")[0].strip("[")
                                    try:
                                        log_time = datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
                                    except ValueError:
                                        log_time = None
                                else:
                                    log_time = None
    
                                # 记录错误详情
                                error_info = {
                                    "file": log_file_path,
                                    "line": line_num,
                                    "time": log_time.strftime("%Y-%m-%d %H:%M:%S") if log_time else "未知",
                                    "message": line
                                }
                                error_records.append(error_info)
    
                                # 提取错误类型(假设错误格式:ERROR: 类型: 描述)
                                if ": " in line[6:]:  # 跳过 "ERROR:" 前缀
                                    error_type = line[6:].split(": ")[0].strip()
                                    error_types.append(error_type)
    
        # 4. 统计错误类型
        error_count = Counter(error_types) if error_types else {}
    
        # 5. 生成报告数据
        report = {
            "report_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "log_dir": log_dir,
            "total_error_count": len(error_records),
            "error_type_statistics": dict(error_count),
            "error_details": error_records
        }
    
        # 6. 保存报告为 JSON 文件
        with open(output_file, "w", encoding="utf-8") as f:
            json.dump(report, f, ensure_ascii=False, indent=2)
    
        print(f"\n日志收集完成!共发现 {len(error_records)} 条错误,报告已保存至:{output_file}")
    
    # 执行日志收集
    if __name__ == "__main__":
        # 日志目录(可替换为实际目录)
        target_log_dir = "./logs"
        # 输出报告文件
        output_json = "error_report.json"
        # 执行收集
        collect_error_logs(target_log_dir, output_json)

四、易错点总结

  1. 模块命名冲突:自定义模块名与标准库(如 os.pyjson.py)或第三方库同名,导致导入错误(解决方案:修改自定义模块名);
  2. 路径拼接错误:直接用 + 拼接路径(如 "./data" + "/logs"),不兼容 Windows/Linux 系统(解决方案:用 os.path.join);
  3. JSON 中文乱码:json.dump 时未指定 ensure_ascii=False,导致中文被转义为 Unicode 编码(解决方案:添加 ensure_ascii=False);
  4. datetime 格式化错误:混用格式化符号(如 %Y 写成 %y%H 写成 %h),导致解析 / 格式化失败(解决方案:熟记常用格式化符号);
  5. dequelist 混淆:用 list 进行头部高频增删操作,导致效率低下(解决方案:改用 collections.deque);
  6. 模块导入路径问题:自定义模块不在当前目录,且未添加到 sys.path,导致导入失败(解决方案:sys.path.append(模块目录));
  7. defaultdict 默认值类型错误:初始化时指定错误的默认值类型(如需要列表却指定 int),导致后续操作报错(解决方案:根据场景选择默认值类型)。
相关推荐
I_ltt_Itw,1 小时前
Python协程学习笔记
开发语言·网络·python
爱笑的眼睛111 小时前
Flask应用API深度开发:从单体架构到微服务设计模式
java·人工智能·python·ai
AI小云1 小时前
【数据操作与可视化】Matplotlib绘图-常用操作
python·数据可视化
axihaihai1 小时前
腾讯云镜像仓库访问问题
linux·服务器·腾讯云
xuanloyer1 小时前
linux基础学习--学习bash
linux·学习·bash
happytree0011 小时前
suricata之Threads
linux
木婉清fresh1 小时前
测开python高频面试精选100题
开发语言·python·面试
彼岸花开了吗1 小时前
构建AI智能体:四十、K-Means++与RAG的融合创新:智能聚类与检索增强生成的深度应用
人工智能·python
嘻哈baby1 小时前
从零搭建家庭All-in-One服务器:300元成本实现企业级功能
运维·服务器