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),导致后续操作报错(解决方案:根据场景选择默认值类型)。
相关推荐
maosheng11465 小时前
RHCSA的第一次作业
linux·运维·服务器
猿界零零七5 小时前
pip install mxnet 报错解决方案
python·pip·mxnet
wifi chicken6 小时前
Linux 端口扫描及拓展
linux·端口扫描·网络攻击
旺仔.2916 小时前
Linux 信号详解
linux·运维·网络
放飞梦想C6 小时前
CPU Cache
linux·cache
Hoshino.417 小时前
基于Linux中的数据库操作——下载与安装(1)
linux·运维·数据库
不只会拍照的程序猿7 小时前
《嵌入式AI筑基笔记02:Python数据类型01,从C的“硬核”到Python的“包容”》
人工智能·笔记·python
恒创科技HK7 小时前
通用型云服务器与计算型云服务器:您真正需要哪些配置?
运维·服务器
Jay_Franklin8 小时前
Quarto与Python集成使用
开发语言·python·markdown
Oueii8 小时前
Django全栈开发入门:构建一个博客系统
jvm·数据库·python