admin管理员组

文章数量:1643853

文章目录

    • 七、模块和包
      • 7.1 模块
        • 7.1.1 模块搜索路径
        • 7.1.2 `PYTHONPATH`和`sys.path`
        • 7.1.2 模块的导入和常见错误
        • 7.1.3 模块的缓存机制
        • 7.1.4 `__name__` 和 `__main__` 函数
      • 7.2 标准库
      • 7.3 包
        • 7.3.1 创建包
        • 7.3.2 导入包
        • 7.3.3 pip包管理器
        • 7.3.4 conda
      • 7.4 如何组织和管理大型项目中的模块与包
        • 7.4.1 最佳实践
        • 7.4.2 Sphinx
    • 八、 I/O系统(文件管理)
      • 8.1 `I/O`简介
      • 8.2 读写文本、二进制文件
        • 8.2.1 open函数
        • 8.2.2 文件指针和文件截断(seek&truncate)
        • 8.2.3 读写函数(read、write)
        • 8.2.4 上下文管理器(with)
      • 8.3 读写excel/csv文件
        • 8.3.1 pandas读写excel/csv文件
        • 8.3.2 openpyxl库
        • 8.3.3 xlrd库(打开加密excel)
      • 8.4 os模块
        • 8.4.1 os模块常用函数
        • 8.4.2 os.walk函数
      • 8.5 shutil 模块(移动、复制、压缩文件)
      • 8.6 Python数据的序列化
        • 8.6.1 数据的序列化和反序列化
        • 8.6.2 json模块
        • 8.6.3 pickle模块
        • 8.6.4 示例:使用pickle保存lightGBM模型&加载预测

本文相关链接:

  • Python官网、Python 3.11.5 中文文档、慕课:Python语言程序设计、Real Python :Write More Pythonic Code
  • Miniconda、Anaconda、conda文档
  • Python 标准库官方文档、 《os — 多种操作系统接口》、《os.path — 常用路径操作》、《shutil — 高阶文件操作》
  • Pandas文档《Input/output》、 openpyxl官方文档、xlrd官方文档、json模块、pickle模块

七、模块和包

  在Python中,模块(Moudle)和包(Package)是组织和管理代码的重要方式。它们有助于将代码分割成可管理的块,提高了代码的可重用性和可维护性。现在,让我们深入了解这两个概念。

7.1 模块

  模块是一个包含Python代码的文件,其文件名是模块名加后缀名 .py。文件中可以包含函数、变量和类,它们被组织在一起以便在其它地方重用。所以我们只需编写Python代码并将其保存为.py文件,就创建了一个模块,其他程序就可以导入它并使用其中的功能。

  模块的文件名应该是一个有效的Python标识符,遵循Python的命名约定,不能与Python的关键字或者与标准库、已安装的第三方库重名,防止冲突。最好使用有描述性的变量、函数和类名,以提高代码的可读性。

7.1.1 模块搜索路径

  在Python中,要使用模块或包中的内容,您需要导入它们。Python解释器在导入模块时会搜索模块的路径,包括以下位置:

  • 当前目录
  • Python标准库的安装位置
  • 环境变量 PYTHONPATH 中指定的目录
  • Python内置的一些默认目录

  这些路径按照优先级的顺序搜索。首先,解释器会查找当前目录是否有所需的模块,然后才会继续搜索其他路径。如果找到匹配的模块,解释器会导入它。

  标准库通常安装在Python的安装目录中,而site-packages目录包含了第三方模块的安装位置。运行以下代码,你将看到Python标准库的安装位置和site-packages目录的路径。

import sys
import site

print("Python标准库的安装位置:")
print(sys.prefix)

print("\nPython的site-packages目录:")
print(site.getsitepackages())
7.1.2 PYTHONPATHsys.path
  1. PYTHONPATH

  环境变量PYTHONPATH允许您自定义模块搜索路径,这对于在项目中使用自定义模块或第三方模块非常有用。你可以使用下面的方式查看和设置PYTHONPATH

  • 在Linux或macOS系统上,可以在终端中执行echo $PYTHONPATH命令来查看PYTHONPATH,使用export PYTHONPATH=/path/to/your/custom/modules以在PYTHONPATH中指定自定义模块的搜索路径。

  • 在windows系统中,可以使用Python代码来访问和配置PYTHONPATH

    import os
    
    pythonpath = os.environ.get('PYTHONPATH')
    if pythonpath:
        print("PYTHONPATH的值:", pythonpath)
    else:
        print("PYTHONPATH未设置")              # 未设置PYTHONPATH时,其值为None
    
    import os
    	
    # 设置PYTHONPATH的值
    new_pythonpath = "/path/to/your/custom/modules"
    os.environ['PYTHONPATH'] = new_pythonpath
    
    # 验证是否已成功设置
    pythonpath = os.environ.get('PYTHONPATH')
    if pythonpath:
        print("已设置的PYTHONPATH的值:", pythonpath)
    else:
        print("PYTHONPATH未设置")
    

    上述代码仅在当前Python进程中更改了PYTHONPATH的值。如果您希望永久更改系统环境变量,需要在系统环境变量中添加。

  1. sys.path

  sys.path是一个包含目录路径的列表,Python解释器在导入模块时会搜索这些路径。以下是如何使用sys.path管理模块搜索路径的示例:

import sys

# 查看当前sys.path的值
print("当前sys.path:", sys.path)

# 添加一个自定义模块路径到sys.path
custom_module_path = "/path/to/your/custom/modules"
sys.path.append(custom_module_path)

# 再次查看sys.path,您将看到新路径已添加
print("更新后的sys.path:", sys.path)

# 现在可以导入自定义模块
import custom_module
  1. sys.pathPYTHONPATH 的区别

  sys.pathPYTHONPATH 都用于管理 Python 模块的搜索路径,但它们之间存在一些重要的区别:

  1. 作用范围

    • sys.path 是 Python 解释器级别的,它仅影响当前 Python 进程中的模块搜索路径。这意味着对 sys.path 的更改仅在当前 Python 程序中生效,不会影响全局环境或其他 Python 进程。

    • PYTHONPATH 是操作系统级别的环境变量,它影响所有 Python 进程的模块搜索路径,包括您在命令行中运行的 Python 脚本、Python IDE 中的代码,以及其他所有 Python 程序。因此,更改 PYTHONPATH 可以全局影响 Python 模块的搜索路径。

  2. 优先级

    • 当导入模块时,sys.path 中的路径优先于 PYTHONPATH 中的路径。这意味着如果在 sys.path 中找到了匹配的模块,将首先使用它,而不会查找 PYTHONPATH 中的路径。

    • 如果没有在 sys.path 中找到模块,Python 解释器将继续查找 PYTHONPATH 中的路径。

    • 如果使用了虚拟环境,虚拟环境会自动管理sys.path,以便在虚拟环境中安装的模块优先于全局模块。这可以确保项目的依赖关系得到良好的隔离和管理。

  3. 动态性

    • 您可以在运行时使用 Python 代码修改 sys.path,以动态添加或删除模块搜索路径,而不需要重新启动 Python 进程。

    • 要更改 PYTHONPATH,您通常需要重新启动与环境变量关联的 Python 进程,这意味着更改可能需要在启动新进程之前生效。

  4. 局部性

    • sys.path 更适合项目级别的管理,您可以在项目代码中使用它来管理模块搜索路径,以确保项目的依赖关系得到良好的隔离。

    • PYTHONPATH 更适合全局配置,用于设置系统范围的模块搜索路径,可能影响多个项目和 Python 进程。

  综上所述,sys.path 更加灵活,适用于在项目级别管理模块搜索路径,不会影响全局环境。而 PYTHONPATH 用于全局配置,影响所有 Python 进程。通常情况下,在项目中使用虚拟环境和 sys.path 更为常见,因为它提供了更好的隔离性和控制,这对于在不同项目中使用不同的模块版本或自定义模块非常有用。

7.1.2 模块的导入和常见错误

Python提供了不同的模块导入方式,以满足不同的需求。

  • 使用 import module_name 导入整个模块。
  • 使用 from module_name import function_name 导入模块中的特定函数或变量。
  • 使用 as 关键字为模块或导入的内容创建别名。

示例:

import math  						 # 导入整个math模块
result = math.sqrt(25) 				 # 使用模块中的函数

from math import sqrt  				 # 导入math模块中的sqrt函数
result = sqrt(25)  					 # 可以直接使用函数,不需要模块前缀

import numpy as np 					 # 导入NumPy库并为其创建一个别名

my_array = np.array([1, 2, 3])  	 # 创建一个NumPy数组

除了使用完整的绝对路径或模块名,也可以使用相对路径来导入模块,这分两种情况:

  1. 相对于当前模块的相对路径导入: 这种方式允许您在同一目录中的模块之间进行相对路径导入。

假设你有以下项目结构:

my_project/
    ├── main.py
    ├── module1.py
    └── module2.py

如果你想从 module1.py 中导入 module2.py,您可以使用相对路径导入如下:

# module1.py

from . import module2  # 使用相对路径导入

我们一般用. 表示当前目录,使用 .. 表示父目录。

  1. 相对于顶级包的相对路径导入
    如果您的项目是一个包(包含一个 __init__.py 文件的目录),您可以使用相对于顶级包的相对路径导入。

假设您有以下项目结构:

my_package/
    ├── __init__.py
    ├── subpackage/
    │     ├── __init__.py
    │     ├── module1.py
    │     └── module2.py
    └── main.py

如果您想从 module1.py 中导入 module2.py,可以使用相对路径导入如下:

# module1.py

from ..subpackage import module2  # 使用相对路径导入

  在这个示例中,.. 表示父包(my_package),然后导入了子包 subpackage 中的 module2.py

  需要注意的是,相对路径导入通常在包内部使用,以便在包内的模块之间进行导入,这可以使项目结构更清晰,但需要谨慎使用,以确保相对路径的正确性。通常更常见的是使用绝对路径导入或直接使用模块名导入。

7.1.3 模块的缓存机制

此部分更多细节及决策流程图,详见 PEP 3147

  为了快速加载模块,Python解释器会缓存已经导入的模块,避免重复导入和提高性能。这意味着模块只会在第一次导入时执行一次,后续导入将使用缓存的版本。

  Python 一般把模块的编译版本缓存在 __pycache__ 目录中,文件名为 module.version.pyc,其中海报好了编译器的版本号,例如CPython 的 3.3 发行版中,spam.py 的编译版本缓存为 __pycache__/spam.cpython-33.pyc。这种命名惯例让不同 Python 版本编译的模块可以共存。

  另外,Python 会对比编译版与源码的修改日期,查看编译版是否已过期,是否要重新编译,此进程是完全自动的,与平台也没有关系,因此,Python可在不同架构的系统之间共享相同的库。

7.1.4 __name____main__ 函数

  __name__ 是一个内置的特殊变量,当模块被导入时,__name__ 的值是模块的名称。但当模块被直接运行时,__name__ 的值是 "__main__",所以它可用于确定模块是以主程序运行还是被导入到其他程序中。

  我们在__main__函数中编写的代码,只有在模块被直接运行时才执行,而在被导入时不执行。假设我们有一个名为 my_module.py 的模块,其中包含以下代码:

# my_module.py

def my_function():
    print("Function called.")

if __name__ == "__main__":
    print("This module is run directly as the main program.")
else:
    print("This module is imported.")

  现在,如果我们在另一个脚本中导入这个模块,只会执行 else 分支,因为此时__name__ 的值是模块的名称:

import my_module  # 输出 "This module is imported."

但如果我们直接运行这个模块, if __name__ == "__main__":,会执行其下的代码块:

python my_module.py  # 输出 "This module is run directly as the main program."

这使得我们可以在模块中包含一些用于测试或执行的代码,而不必担心它们会在导入时执行。

7.2 标准库

详见Python 标准库官方文档

  库是一组相关的模块的集合,通常具有特定的领域或功能。库也可以称为包。标准库(Standard Library)是Python内置的一组模块和功能,Python生态系统的核心部分。这些模块和功能提供了各种常见任务和操作的标准解决方案。Windows 版本的 Python 安装程序通常包含整个标准库,往往还包含许多额外组件。详情请参考官方文档。

7.3 包

  包是一个包含多个模块的目录。它允许将相关的模块组织在一起,形成一个层次结构。包有助于更好地组织大型项目的代码。它们还可以避免命名冲突,因为每个包内的模块都有自己的命名空间。

7.3.1 创建包

  创建包只需创建一个包含__init__.py文件的目录,并在其中放置模块文件。这个__init__.py文件可以为空,但它表明这是一个包。下面详细介绍一下 __init__.py文件的作用。

  • 标识一个目录作为包

    如果你希望将一个目录视为Python的包(package),那么这个目录中必须包含一个名为 __init__.py 的文件,Python会根据这个文件来判断这个目录是否应该被当作一个包来处理。

  • 防止出现目录名称与模块名称冲突的情况。

    例如,如果没有 __init__.py 文件,Python可能会将一个名为string的目录误认为是一个包,从而导致后续搜索模块时出现问题。

  • 初始化包的某些功能

    __init__.py 也可以包含Python代码,这可以是在导入包时自动执行的初始化代码,或者定义包的公共接口。例如,你可以设置特殊变量 __all__ ,它主要用于控制模块的公共接口,指定在使用 from package import * 语句时,一个模块或包中哪些属性、函数、类等应该被导入到其他模块中。以下是关于__all__的详细介绍:

  1. 作用

    • __all__ 的作用是限制通过 from module import * 语句导入的内容,以避免导入不需要的标识符,同时允许开发者明确指定哪些标识符应该是模块的公共接口。
  2. 使用方式

    • __all__ 是一个包含字符串的列表(List),其中的每个字符串都是模块内部定义的标识符的名称。

    • 在一个模块的顶层,你可以定义 __all__ 变量, 这将明确指定了在 from module import * 语句中可以被导入的标识符。假设有一个模块 my_module.py 包含以下内容:

      def public_function():
          pass
      
      def _private_function():
          pass
      
      class PublicClass:
          pass
      
      class _PrivateClass:
          pass
      
      __all__ = ['public_function', 'PublicClass']
      
    • 如果使用 from my_module import * 语句,只有 public_functionPublicClass 会被导入,而 _private_function_PrivateClass 不会被导入。

    • 如果使用 from my_module import _private_function,则可以导入 _private_function,因为它在 __all__ 中没有被列出。

  3. 注意事项

    • __all__ 不是Python的强制性规定,而是一种约定。它主要用于指导其他开发者使用模块时应该导入哪些内容。

    • 单下划线 _ 开头的标识符通常被认为是私有的,不应该被直接导入,但仍然可以在需要的情况下导入。

  总之,__all__ 是一种用于控制模块或包公共接口的方式,它有助于提高代码的可维护性和可读性,同时避免导入不必要的标识符。

7.3.2 导入包

  我们同样使用import语句,并指定包名和模块名来导入包中的模块。包可以形成层次结构,即包含子包和子包下的模块,只需指定完整的路径,也可以完成导入。一个常见的项目结构可能如下所示:

my_project/
    ├── __init__.py
    ├── module1.py
    ├── module2.py
    └── subpackage/
        ├── __init__.py
        ├── submodule1.py
        └── submodule2.py
# module1.py

from ..subpackage import module2  # 使用相对路径导入

  在实际使用中,尽量避免使用 from module import *,最好是明确导入需要的标识符,例如 from module import func1, class1,因为这会产生两个问题:

  1. 污染命名空间(Namespace Pollution)

    • 当你使用 from module import * 时,Python会将该模块中的所有标识符都导入到当前的命名空间中,包括模块中可能没有被明确设计为公共接口的标识符。这意味着你的命名空间会变得非常拥挤,充斥着许多你可能并不需要的标识符,即命名空间污染。
    • 这些额外的标识符可能会与你的代码中已经定义的标识符产生冲突,导致不可预测的行为或错误。
  2. 代码难以理解

    • 当你在代码中看到大量的 from module import * 语句时,很难确定哪些标识符实际上是来自于这个模块,因为它们并没有明确地被列出。这会使代码变得难以理解和维护,因为你无法快速识别哪些标识符来自哪个模块。
    • 同时,如果你使用了多个 from module import * 语句,不同模块中的标识符可能会相互覆盖,进一步增加了混乱和错误的可能性。
7.3.3 pip包管理器

  pip是Python包管理器,专用于安装、升级和管理Python包。以下是常用的pip命令:

pip命令描述
pip --version验证pip是否安装并显示版本号
pip list列出已安装的所有包及其版本号
pip install --upgrade pip升级pip到最新版本
pip install package_name安装包
pip install package_name==desired_version安装特定版本的包
pip install --upgrade package_name升级指定包
pip uninstall package_name卸载指定包
pip freeze > requirements.txt将当前环境的包列表保存到requirements.txt文件中
pip install -r requirements.txt从requirements文件安装所有依赖项
pip show package_name显示指定包的详细信息,包括版本和安装路径
虚拟环境相关命令描述
pip install virtualenv安装虚拟环境模块(如果需要的话)
python -m venv myenv创建新的虚拟环境(myenv是虚拟环境的名称)
myenv\Scripts\activate(Windows)激活虚拟环境(Windows上)
source myenv/bin/activate(macOS和Linux)激活虚拟环境(macOS和Linux上)
deactivate退出虚拟环境
其它命令(不常用)描述
pip check检查当前环境的包是否存在问题
pip search query搜索包含指定关键字的包
pip download package_name下载指定包的源代码,但不安装
pip install --user package_name在用户级别安装包,而不是全局或虚拟环境
pip install --no-cache-dir package_name安装包时禁用缓存,用于解决一些安装问题
7.3.4 conda

Miniconda、Anaconda、conda文档

  venv 是 Python 的标准库中自带的虚拟环境管理工具,只包含与 Python 环境相关的必要组件。这使得它适合于创建纯粹的 Python 环境,即不支持非Python语言的包。所以目前一般是使用conda来管理虚拟环境。

  conda 是一个跨平台的虚拟环境管理器,不仅支持 Python,还支持其他编程语言,也可以管理系统依赖项,目前有两个版本——Miniconda和Anaconda。前者只有几百M,是最小化版本,只包括Conda和Python,用户可以自定义需要安装的其它包。而后者有5、6G,预装了许多包,适用于各种数据科学任务。

以下是一些常用的conda命令:

conda包管理命令描述
conda create --name myenv python=3.8创建名为myenv的虚拟环境,指定python版本为3.8
conda activate myenv
source activate myenv
激活虚拟环境(windows)
激活虚拟环境(macOS和Linux)
conda install package_name在激活的虚拟环境中安装Python包
conda list列出当前虚拟环境中已安装的包
conda deactivate停用当前虚拟环境
conda env export > environment.yml导出当前虚拟环境的配置到一个YAML文件
conda env create -f environment.yml根据YAML文件创建虚拟环境
conda remove --name myenv --all删除指定名称的虚拟环境及其所有包
conda search package_name搜索可用于安装的包
conda update --all升级当前虚拟环境中的所有包
conda虚拟环境管理命令描述
conda update conda升级conda本身
conda config --show显示conda的配置信息
conda env list列出所有已创建的虚拟环境
conda info --env显示当前虚拟环境的详细信息
conda config --set auto_activate_base false禁用默认激活基础环境(默认情况下会自动激活基础环境)
conda config --set auto_activate your_env_name设置your_env_name为默认的激活环境

  默认情况下,conda自动激活base环境为当前使用环境。如果要更改某个环境为默认激活环境,你需要进行一下操作:

conda config --set auto_activate_base false				# 禁用默认激活基础环境
conda config --set auto_activate your_env_name			# 设置your_env_name为默认的激活环境

如果要恢复默认激活base环境,需要运行:

conda config --set auto_activate_base true 				# 恢复默认激活base环境

7.4 如何组织和管理大型项目中的模块与包

7.4.1 最佳实践

  在大型项目中,组织和管理模块与包是非常重要的,它有助于代码的可维护性和可扩展性。以下是一些最佳实践:

1. 项目结构:

  • 使用包(即包含__init__.py文件的目录)来组织相关的模块。包可以将相关模块放在一起,形成层次结构,使代码更有结构性。

2. 模块和包的命名:

  • 给模块和包起一个清晰、有意义的名称,遵循Python的命名约定。避免使用与Python内置模块或库相同的名称,以防止命名冲突。
  • 使用有描述性的变量、函数和类名,以提高代码的可读性。

3. 文档:

  • 为模块、函数和类编写文档字符串(docstrings)。文档字符串应解释模块或代码的用途、输入参数、返回值等信息。这有助于其他开发人员理解和使用您的代码。
  • 使用工具如Sphinx来生成文档,以便更轻松地维护和分享文档。
  • 维护更新的文档,包括项目的使用方法、配置和安装说明。

文档字符串的说明可参考《python3.11教程1》4.6章

# numpy.py

"""
NumPy is the fundamental package for scientific computing in Python.
It provides support for arrays, mathematical functions, linear algebra, and more.

For more information, visit: https://numpy
"""

import numpy as np

def create_array(shape, dtype=float):
    """
    Create a new array of the given shape and data type.
    
    Parameters:
    shape (tuple): The shape of the array.
    dtype (type): The data type of the elements in the array (default is float).
    
    Returns:
    ndarray: A new NumPy array.
    """
    return np.zeros(shape, dtype=dtype)

4. 版本控制:

Git官方文档、Git中文教程、Atlassian Git教程

  • 尽量避免模块之间的循环依赖。循环依赖可能导致难以调试和维护的问题。
  • 使用版本控制系统(如Git)来跟踪和管理代码的变化。这有助于团队合作、版本控制和错误修复。
  • 使用合适的版本号管理约定(如Semantic Versioning)来管理您的项目的版本。

5. 自动化构建和测试:

  • pytest官方文档、W3Cschool《pytest教程》、realpython网教程《Effective Python Testing With Pytest》
  • bilibili视频 及 视频博客——pytest 框架
  • 编写单元测试来验证模块和函数的正确性。这有助于捕获潜在的错误,并确保代码在修改后仍然正常工作。
  • 使用测试框架(如unittest或pytest)来自动运行测试。

6. 依赖管理:

  • 使用依赖管理工具(如pip或conda)来管理项目的依赖关系。这有助于确保项目在不同环境中的一致性。
  • 使用虚拟环境来隔离不同项目的依赖,确保项目使用的库与其他项目不会发生冲突。

7. 可扩展性和性能优化:

  • 在设计项目时,考虑性能和可扩展性。选择适当的数据结构和算法,以便项目能够在处理大量数据时保持高效。

8. 团队协作:

  • 如果您在团队中工作,请遵循共同的编码约定和工作流程。使用代码审查来确保代码质量。
7.4.2 Sphinx

  Sphinx 是一个用于生成文档的工具,特别是用于编写和发布Python项目的文档。它是一个强大的文档生成器,支持多种输出格式,包括HTML、PDF、ePub等,以及集成了自动化的构建工具。Sphinx 最初由Python社区创建,但现在已广泛用于其他编程语言和项目的文档生成。以下是 Sphinx 的一些主要特点和用途:

  1. 结构化文档:Sphinx 使用一种称为reStructuredText(或简称 reST)的标记语言,允许您编写结构化的文档。这使得文档易于阅读、维护和版本控制。

  2. 自动索引:Sphinx 可以自动生成文档的索引、目录和交叉引用,使用户能够轻松导航文档。

  3. 多种输出格式:Sphinx 支持多种输出格式,包括HTML、PDF、ePub、man页、LaTeX等。

  4. 扩展性:Sphinx 具有丰富的扩展和插件系统,允许您自定义文档生成的过程,添加自定义样式和功能。

  5. 集成版本控制:Sphinx 可以与版本控制系统(如Git)集成,使您能够轻松将文档与代码同步,以确保文档的实时性。

  6. 支持多语言:Sphinx 支持多语言文档,允许您为不同的受众创建多语言版本的文档。

  7. 应用广泛:Sphinx 在许多开源项目中广泛使用,包括Python自身的官方文档、Django、numpy、pandas等。

  总的来说,Sphinx 是一个功能强大的文档生成工具,特别适用于编写和发布Python项目的文档。它的灵活性和丰富的功能使其成为开发者、项目维护者和技术作者的首选工具之一。以下是一些关于如何使用 Sphinx 的具体教程、博客和示例项目:

  1. Sphinx 官方文档

    Sphinx 的官方文档是使用 Sphinx 编写的,它包含了完整的 Sphinx 使用指南。这是开始使用 Sphinx 的最佳起点。

  2. Real Python Course: Documenting Python Projects With Sphinx and Read the Docs

    在本视频系列中,将使用 Sphinx ( Python 的实际标准)从头开始创建项目文档、将代码存储库连接到 Read The Docs,以自动生成和发布代码文档。

  3. The Sphinx Book

    这是一本关于如何使用 Sphinx 编写书籍和长篇文档的免费在线书籍。它提供了详细的步骤和示例。

  4. Sphinx Gallery

    Sphinx Gallery 是一个示例项目,展示了如何使用 Sphinx 来创建一个包含代码示例、文档和说明的画廊。这对于展示您的代码如何工作非常有用。

八、 I/O系统(文件管理)

8.1 I/O简介

  文件输入和输出,通常缩写为文件I/O,是计算机编程中一个重要的概念。它涉及到将数据从程序读取到文件(输入)或从文件写入到程序(输出)。文件I/O对于存储和检索数据以及与外部世界进行交互非常关键。

文件I/O对于许多任务至关重要,例如:

  • 数据持久性: 文件是一种持久性存储方法,可以将数据保存在磁盘上,以便在程序重新运行时保留数据。

  • 数据共享: 文件允许不同的程序或多个实例之间共享数据。多个程序可以读取和写入同一个文件。

  • 数据备份: 文件允许您创建数据的备份,以防数据丢失或损坏。

  • 外部数据交换: 您可以使用文件I/O将数据与其他应用程序或系统进行交换。这在数据导入和导出时非常有用。

  • 记录日志: 许多应用程序使用文件来记录日志信息,以帮助故障排除和监视应用程序的运行情况。

以下是一些基本的文件I/O操作:

  • 使用open()函数打开文件。

  • 使用.read()方法从文件中读取数据。

  • 使用.write()方法将数据写入文件。

  • 使用with语句来自动关闭文件,以确保资源的正确管理。

8.2 读写文本、二进制文件

官方文档:open函数、 with 上下文管理器

对于文本或二进制文件,我们可以使用open、read和write函数来进行读写,下面是一个简单的示例:

# 打开一个文本文件以供写入
with open("example.txt", "w") as file:
    file.write("这是一个示例文本。")
8.2.1 open函数

open函数常用于打开文件,其语法为:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

常用参数解析:

  1. file(必需):这是要打开的文件的名称(包括路径),可以是相对路径或绝对路径。如果文件不存在,根据所选的模式来操作。

  2. mode(可选):这是文件打开的模式,它表示您要对文件执行的操作。默认是只读模式(‘r’)。

模式描述
'r'只读模式,文件只能被读取(默认模式)。
'w'写入模式,如果文件存在,则截断文件并写入(覆盖原先内容)。如果文件不存在,则创建新文件并写入
'a'追加模式,用于在文件末尾添加内容。如果文件不存在,则创建新文件。
'x'独占创建模式,文件不存在时创建新文件。如果文件已存在,则抛出错误
'b'二进制模式,与上述模式结合使用,以指示以二进制格式打开文件(例如:‘rb’、‘wb’、‘ab’)。
't'文本模式(默认模式)。。与上述模式结合使用,以指示以文本模式打开文件(例如:‘rt’、‘wt’、‘at’)
'+'读写模式,用于同时读取和写入文件。
# 写入二进制文件
with open("new_image.jpg", "wb") as new_binary_file:
    new_binary_file.write(image_data)

  '+'是读写模式,可以在同一文件上执行读取和写入操作,而不必先关闭文件再重新打开,适用于需要读取和更新文件内容的情况。例如:

example ='Line 1: This is the first line.\n''Line 2: This is the second line.'

# 以读写模式打开空白文件
with open("example.txt", "w+") as file:
	content = file.read()
    print("原始内容:", content)
    
    file.write(example)						# 在文件中写入新数据
    file.seek(0)							# 回到文件开头
    updated_content=file.read()				# 读取更新后的内容
    print(updated_content)

  请注意,使用 '+' 模式时,要小心处理文件指针的位置以确保读取和写入操作发生在您希望的位置。

  1. encoding(可选):用于文本文件的字符编码。默认情况下,Python将使用系统默认编码。常见的编码包括 'utf-8''ascii' 等。

  2. errors(可选):处理编码错误的方式,默认是 None,即使用默认错误处理方式。

error选项说明
'strict'默认选项,表示严格处理编码错误,遇到无法解码的字节序列将引发UnicodeDecodeError异常。
'ignore'忽略无法解码的字节序列,不会引发异常(无法解码的部分将被丢弃)。
'replace'使用 Unicode 替代字符 U+FFFD(�)来替换无法解码的字节。
'xmlcharrefreplace'使用 XML 字符引用来替换无法解码的字节,对于生成包含无效字符的 XML 数据很有用
'backslashreplace'使用反斜杠和十六进制编码的字节值来替换无法解码的字节。
'namereplace'使用 Unicode 转义序列来替换无法解码的字节,这样可以保留无效字符的名称信息。
  1. opener(可选):自定义打开文件的函数,例如:
# 使用自定义的打开函数my_opener打开文件
def my_opener(file, flags):
    return os.open(file, flags)

file = open("example.txt", "r", opener=my_opener)
8.2.2 文件指针和文件截断(seek&truncate)

  文件定位和截断是处理日志文件、配置文件、数据库文件等常见用例中的重要工具。它们允许您以灵活的方式管理文件的内容。

  1. seek函数

  在 Python 中,文件指针是与文件关联的位置标识符,它指示了在文件中读取或写入数据时的当前位置。文件指针是一个整数,表示文件中的字节偏移量,其语法为:

file.seek(offset, whence)
  • offset :一个整数,表示要移动的字节偏移量,默认为0。
  • whence :一个整数,表示相对位置,选项为:
    • 0 表示相对于文件的开头(默认值)。
    • 1 表示相对于文件指针的当前位置。
    • 2 表示相对于文件的末尾。

文件打开时文件指针默认处于文件开头,你也可以自由设置:

# 将文件指针移动到文件开头的第 100 字节处
file.seek(100, 0)

# 将文件指针从当前位置向后移动 50 字节
file.seek(50, 1)

# 将文件指针移动到文件末尾前的倒数第 200 字节处
file.seek(-200, 2)
  1. truncate函数

  文件截断是指删除文件中的部分内容,使文件变得更短或清空文件,你可以使用truncate来执行此操作,其语法为:

file.truncate(size)

  其中,size表示截断时指定的大小(字节单位)。如果省略size参数,则文件将被截断为当前文件指针位置之前的大小,例如:

with open("file.txt", "r+") as file:
    file.truncate(50)  # 截断文件为前50个字节
  1. 示例:在文件中插入数据
with open("file.txt", "r+") as file:
    # 移动文件指针到位置 30(假设文件足够长)
    file.seek(30)
    
    # 读取文件中剩余的内容
    remaining_data = file.read()
    
    # 回到位置 30 并插入新文本
    file.seek(30)
    file.write("插入的新文本 ")
    
    # 写入原始内容
    file.write(remaining_data)

这将在文件的指定位置插入新文本,并保留原始内容。

  1. 注意事项
  • 使用seek()truncate()时,请小心不要超出文件的范围,以免发生错误。

  • 在使用truncate()时,文件必须以可写入(“w"或"r+”)模式打开。否则,将引发UnsupportedOperation异常。

  • 文件截断是不可逆的操作,删除的数据将无法恢复。谨慎使用。

8.2.3 读写函数(read、write)

以下是Python中的文件读写函数:

方法描述参数
read(size)读取整个文件的内容为,返回一个字符串。size可选,指定要读取的最大字节数。未指定时默认读取整个文件。
readline(size)逐行读取文件的内容,即每次读取一行。size可选,指定一行中要读取的最大字节数。未指定时默认读取整行内容。
readlines(hint)默认读取整个文件的内容,并将每行存储为列表中的一个元素。hint可选,含义同上
for 循环逐行读取使用 for 循环逐行读取文件的内容。
write(str)根据选择的模式,将指定的字符串写入文件的当前位置。write函数没有额外参数
close关闭文件,释放文件资源并确保数据被保存。

read和readlines的区别:

  • read(size):读取前size个字节的数据,不关心行的分隔符,最后返回一个字符串。另外文件指针将保持在读取数据之后的位置。
  • readlines(size):读取前size个字节的数据,包含分隔符(通常是换行符 \n),返回字符串列表(每一行)。最后,文件指针将移到下一个未读行的开头。

示例:

"""
example.txt 
Line 1: This is the first line.
Line 2: This is the second line.
"""

file = open('example.txt', 'r')
data1 = file.read(20)  				 # 读取前 20 个字节,结果:"Line 1: This is the "
data2 = file.readlines(30)  		 # 读取前 30 个字节,结果:["first line.\n", "Line 2: This is the second line."]

file.close()
8.2.4 上下文管理器(with)

官方文档: with 上下文管理器

  使用上下文管理器非常简单,只需在 with 语句中创建并使用它即可。下面是一个使用上下文管理器的示例,以打开和关闭文件为例:

# 使用上下文管理器自动关闭文件
with open("example.txt", "r") as file:
    content = file.read()
    print(content)

# 在此处文件已自动关闭,无需手动关闭

  在这个示例中,open() 函数返回一个文件对象,它是一个上下文管理器。当进入 with 代码块时,__enter__() 方法被调用,文件被打开。当代码块执行完毕后,无论正常执行还是出现异常,__exit__() 方法都会被调用,确保文件被正确关闭。

  你还可以创建自定义的上下文管理器,以管理其他资源(如数据库连接、网络连接等)。要创建自定义上下文管理器,只需定义一个类,并实现 __enter__()__exit__() 方法。以下是一个示例:

# 创建MyContextManager类实现一个简单的上下文管理器
class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        # 在此处可以初始化资源
        return self  # 返回一个对象(可选)

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        # 在此处进行清理操作,如关闭资源

# 使用自定义上下文管理器
with MyContextManager() as cm:
    print("Inside the context")

# 在此处自定义上下文管理器的__exit__()方法被调用,进行资源清理

  上下文管理器是一个非常强大和有用的Python功能,它确保了资源的正确分配和释放,使代码更加健壮和可维护。无论是使用内置的上下文管理器还是创建自定义的上下文管理器,都可以提高代码的可读性和可维护性。

8.3 读写excel/csv文件

参考pandas官方文档《Input/output》、openpyxl官方文档、xlrd官方文档

  前面讲的都是读写文本文件和二进制文件,如果要处理excel或者csv文件,可以使用pandasku、openpyxl库或者xird,下面进行简单介绍。

8.3.1 pandas读写excel/csv文件

pandas 可以读取的文件格式有很多,下面一一介绍。
1. 读取 csv, excel, txt 文件

pandas.read_csv(filepath, *, sep=_NoDefault.no_default, delimiter=None, header='infer', names=_NoDefault.no_default, index_col=None, usecols=None, dtype=None, engine=None, converters=None, true_values=None, false_values=None, skipinitialspace=False, skiprows=None, skipfooter=0, nrows=None, na_values=None, keep_default_na=True, na_filter=True, verbose=False, skip_blank_lines=True, parse_dates=None, infer_datetime_format=_NoDefault.no_default, keep_date_col=False, date_parser=_NoDefault.no_default, date_format=None, dayfirst=False, cache_dates=True, iterator=False, chunksize=None, compression='infer', thousands=None, decimal='.', lineterminator=None, quotechar='"', quoting=0, doublequote=True, escapechar=None, comment=None, encoding=None, encoding_errors='strict', dialect=None, on_bad_lines='error', delim_whitespace=False, low_memory=True, memory_map=False, float_precision=None, storage_options=None, dtype_backend=_NoDefault.no_default)[source]
df_csv = pd.read_csv('data/my_csv.csv')
df_txt = pd.read_table('data/my_table.txt')
df_excel = pd.read_excel('data/my_excel.xlsx')

这里有一些常用的公共参数:

  • filepath:可以是文件路径或者URL,例如本地文件file://localhost/path/to/table.csv。
  • sep:分割符。pd.read_csv 默认使用逗号作为分隔符,pd.read_table 默认使用制表符\t作为分隔符。
  • header='infer': 指定用作列名的行号,默认为0,表示第一行。可以设置header=None不使用列名。
  • names:自定义列名,以列表形式提供。如果设置了header=None,则可以使用该参数
  • index_col :表示把某一列或几列作为列索引,可以是整数、字符串或列表
  • usecols :表示要读取的列,可以是列号、列名或者列名的列表,默认读取所有的列
  • parse_dates :指定要解析为日期时间的列,可以是列号或列名的列表
  • date_parser:使用自定义函数解析日期时间列
  • nrows :表示读取的数据行数。
  • skiprows :要跳过的行数(从文件顶部开始计算)
  • skipfooter :要从文件底部跳过的行数(不支持所有的解析器)
  • na_values :用于将特定值识别为缺失值的列表或字典

下面是具体的示例:

# 1.读取txt文件,不设置列名
pd.read_table('data/my_table.txt', header=None)
Out[10]: 
      0     1     2     3		4
0  col1  col2  col3    col4   col5
1     2     a   1.4   apple 2020/1/1
2     3     b   3.4  banana 2020/1/2
3     6     c   2.5  orange 2020/1/5
4     5     d   3.2   lemon 2020/1/7

# 2.设置索引列,并跳过前两行
pd.read_csv('data/my_csv.csv', index_col=['col1', 'col2'],skiprows=[0, 1])
Out[11]: 
           col3    col4      col5
col1 col2                        
6    c      2.5  orange  2020/1/5
5    d      3.2   lemon  2020/1/7

# 3.只读取前两列,并设置列名
pd.read_table('data/my_table.txt', usecols=['col1', 'col2'],header=None,names=[1','2'])
Out[12]:12
0     2    a
1     3    b
2     6    c
3     5    d

# 4.使用自定义函数解析指定'col5'的日期解析格式,只读前两行
date_parser = lambda x: pd.to_datetime(x, format='%Y-%m-%d')
pd.read_csv('data/my_csv.csv', parse_dates=['col5'], date_parser=date_parser,nrows=2)
Out[13]: 
   col1 col2  col3    col4       col5
0     2    a   1.4   apple 2020-01-01
1     3    b   3.4  banana 2020-01-02

# 5. 将文件中的'N/A', 'NA'字符视为缺失值,并替换为NaN
pd.read_csv('data/my_table.txt',header=None,na_values=['N/A', 'NA'])

另外read_excel还有一些常用参数:

  • sheet_name:指定要读取的工作表名称,默认为0,表示第一个工作表。可以使用字符串(工作表名称)或整数(工作表索引)。
  • engine:指定要使用的解析引擎,例如’xlrd’或’openpyxl’。默认根据文件扩展名自动选择

  在读取 txt 文件时,经常遇到分隔符非空格的情况, read_table 有一个分割参数 sep ,它使得用户可以自定义分割符号,进行 txt 数据的读取。例如,下面的读取的表以 |||| 为分割:

pd.read_table('data/my_table_special_sep.txt')
Out[15]: 
              col1 |||| col2
0  TS |||| This is an apple.
1    GQ |||| My name is Bob.
2         WT |||| Well done!

上面的结果显然不是理想的,这时可以使用 sep ,同时需要指定引擎为 python

pd.read_table('data/my_table_special_sep.txt',
              sep=' \|\|\|\| ', engine='python')
Out[16]: 
 col1               col2
0   TS  This is an apple.
1   GQ    My name is Bob.
2   WT         Well done!
3   PT    May I help you?

  在使用 read_table 的时候需要注意,参数 sep 中使用的是正则表达式,因此需要对 | 进行转义变成 \| ,否则无法读取到正确的结果。

2. 写入csv, excel, txt 文件

  pandas 中没有定义 to_table 函数,但是 to_csv 可以保存为 txt 文件(也可以保存csv文件),并且允许自定义分隔符,常用制表符 \t 分割:

# 当索引没有特殊意义的时候,可以使用index=False 去除索引
df.to_csv('data/my_txt_saved.txt', sep='\t', index=False)

  如果想要把表格快速转换为 markdown 和 latex 语言,可以使用 to_markdownto_latex 函数(需要先安装 tabulate 包)。

print(df_csv.to_markdown())
col1col2col3col4col5
02a1.4apple2020/1/1
13b3.4banana2020/1/2
26c2.5orange2020/1/5
35d3.2lemon2020/1/7
print(df_csv.to_latex())
\begin{tabular}{lrlrll}
\toprule
{} &  col1 & col2 &  col3 &    col4 &      col5 \\
\midrule
0 &     2 &    a &   1.4 &   apple &  2020/1/1 \\
1 &     3 &    b &   3.4 &  banana &  2020/1/2 \\
2 &     6 &    c &   2.5 &  orange &  2020/1/5 \\
3 &     5 &    d &   3.2 &   lemon &  2020/1/7 \\
\bottomrule
\end{tabular}
8.3.2 openpyxl库

  openpyxl 是一个非常强大和广泛使用的库。它允许你读取、写入和操作Excel文件(.xlsx 格式)。使用之前,需要进行安装:pip install openpyxl。下面列举openpyxl的主要操作:

操作/函数描述
openpyxl.load_workbook(filename)打开一个Excel工作簿并返回工作簿对象。
workbook.create_sheet(title)创建一个新的工作表并返回工作表对象。
workbook.active获取或设置当前活动的工作表。
workbook.sheetnames返回工作簿中所有工作表的名称列表。
worksheet.cell(row, column)获取或设置指定行列的单元格。
worksheet.iter_rows()遍历工作表中的行,返回单元格值的生成器。
worksheet.iter_cols()遍历工作表中的列,返回单元格值的生成器。
worksheet.title获取或设置工作表的名称。
worksheet.max_row获取工作表中的最大行数。
worksheet.max_column获取工作表中的最大列数。
worksheet['A1']使用字母和数字索引获取单元格的值。
worksheet.append([data])向工作表追加一行数据。
worksheet.delete_rows(index, count)删除工作表中指定索引位置的行。
worksheet.insert_rows(index, count)在指定位置插入指定数量的空白行。
worksheet.delete_cols(index, count)删除工作表中指定索引位置的列。
worksheet.insert_cols(index, count)在指定位置插入指定数量的空白列。
workbook.save(filename)保存工作簿到指定文件。
workbook.remove(sheet)从工作簿中移除指定工作表。
workbook.copy_worksheet(sheet)复制工作表到同一工作簿或另一个工作簿。
import openpyxl

# 打开一个现有的工作簿
workbook = openpyxl.load_workbook('example.xlsx')

# 选择工作簿中的工作表(通过名称)
worksheet = workbook['Sheet1']

# 读取单元格数据
value = worksheet.cell(row=1, column=1).value

# 写入单元格数据
worksheet.cell(row=2, column=2, value='Hello, World!')

# 保存工作簿
workbook.save('new_example.xlsx')

# 创建新的工作表
new_sheet = workbook.create_sheet('NewSheet')

# 遍历工作表数据
print('工作表数据:')
for row in worksheet.iter_rows(values_only=True):
    print(row)
8.3.3 xlrd库(打开加密excel)

xlrd 也是一个用于读取 Excel 文件的 Python 库,主要函数如下:

操作/函数描述
xlrd.open_workbook(filename)打开一个 Excel 工作簿并返回工作簿对象。
workbook.sheet_names()获取工作簿中所有工作表的名称。
workbook.sheet_by_name(name)根据工作表名称选择工作表。
workbook.sheet_by_index(index)根据索引选择工作表。
worksheet.cell_value(row, col)读取指定行列的单元格内容。
worksheet.nrows获取工作表的行数。
worksheet.ncols获取工作表的列数。
worksheet.row(row_num)返回指定行的所有单元格内容(以列表形式)。
worksheet.col(col_num)返回指定列的所有单元格内容(以列表形式)。
xlrd.xldate_as_datetime(date, datemode)将 Excel 日期值转换为 Python 的 datetime 对象。
workbook.close()关闭工作簿。
# 导入所需的库和模块
import pandas as pd
import numpy as np
import os
from xlrd import *
import win32com.client
import sys

# 设置要打开的Excel文件的文件路径
filename = '缩孔数据库/2023缩孔统计更新至9-4.xlsm'
color = ['QAC', 'G41', 'QAB', 'KAD']						# 设置要查找的颜色
xlApp = win32com.client.Dispatch("Excel.Application")		# 创建一个Excel应用程序对象
password = 'xxxxxxxx'										# Excel文件的密码(如果有密码保护的话)

# 使用只读模式打开工作簿
# Format=None表示让 Excel 自动识别文件格式。你也可以明确指定,例如 Format=1 表示打开的是 xls 格式文件。
xlwb = xlApp.Workbooks.Open(Filename=os.path.abspath(filename),
                            ReadOnly=True,
                            Format=None,
                            Password=password)

	
xlws = xlwb.Sheets(3)										# 选择工作簿中的第3张工作表(索引从1开始)
cols = 15													# 选择要读取的列数
rows = xlws.UsedRange.Rows.Count							# 获取工作表的行数

# 从工作表中读取(1, 1)到(rows, cols)之间的单元格,其实就是前15列
content = list(xlws.Range(xlws.Cells(1, 1), xlws.Cells(rows, cols)).Value)

# 将读取的数据转换为Pandas DataFrame
df = pd.DataFrame(content)

8.4 os模块

8.4.1 os模块常用函数

官方文档:《os — 多种操作系统接口》、《os.path — 常用路径操作》

  如果你只是想读写一个文件,请参阅 open(),如果你想操作文件路径,请参阅 os.path 模块,如果你想读取通过命令行给出的所有文件中的所有行,请参阅 fileinput 模块。 为了创建临时文件和目录,请参阅 tempfile 模块,对于高级文件和目录处理,请参阅 shutil 模块。

  os 模块是 Python 的标准库之一,提供了与操作系统交互的功能。它允许您执行与文件系统、目录、文件路径等相关的操作。以下是 os 模块的一些主要功能:

文件和目录操作函数描述
os.mkdir(path)创建一个新的目录。
os.rmdir(path)删除一个空目录。
os.remove(path)删除文件。
os.rename(src, dst)重命名文件或目录。
os.getcwd()返回当前工作目录。
os.listdir(path)返回目录中的文件和子目录的列表。
os.walk(top, topdown=True, onerror=None, followlinks=False)生成目录树中的文件名。
os.chmod(path, mode)更改文件或目录的权限。
os.stat(path)获取文件或目录的状态信息。
路径操作函数描述
os.path.join(path1, path2, ...)连接路径的各个部分,以创建完整路径。
os.path.abspath(path)返回绝对路径。
os.path.exists(path)检查文件或目录是否存在。
os.path.isdir(path)检查路径是否为目录。
os.path.isfile(path)检查路径是否为文件。
os.path.basename(path)返回路径中的文件名。
os.path.dirname(path)返回路径中的目录名。
os.path.split(path)分割路径,返回目录名和文件名的元组。
环境变量函数描述
os.environ包含系统环境变量的字典。
os.getenv(key, default)获取指定环境变量的值。
系统命令函数描述
os.name返回操作系统的名称(例如,‘posix’ 或 ‘nt’)。
os.uname()返回有关当前系统的详细信息(仅在 POSIX 系统上可用)。
os.system(command)在子shell中执行系统命令。
os.spawn*()生成新进程。
os.kill(pid, signal)终止进程
8.4.2 os.walk函数

  os.walk() 是一个非常实用的函数,用于在目录树中遍历文件和子目录。它返回一个生成器,该生成器产生三元组 (dirpath, dirnames, filenames),其中:

  • dirpath:当前目录的路径。
  • dirnames:当前目录下的子目录列表,如果没有子目录,则返回空列表
  • filenames:当前目录下的文件列表。

下面是一个详细说明 os.walk() 用法的示例,假设我们有以下目录结构:

my_folder
│
├── dir1
│   ├── file1.txt
│   └── file2.txt
│
├── dir2
│   ├── sub_dir
│   │   ├── file3.txt
│   │   └── file4.txt
│   └── file5.txt
│
└── file6.txt

我们可以使用 os.walk() 遍历这个目录树并打印出每个目录中的子目录和文件:

import os

# 设置要遍历的根目录
root_directory = 'my_folder'

# 使用 os.walk() 遍历目录树
for dirpath, dirnames, filenames in os.walk(root_directory):
    # 打印当前目录路径
    print(f"Current Directory: {dirpath}")

    # 打印当前目录下的子目录
    print("Subdirectories:")
    for dirname in dirnames:
        print(f" - {dirname}")

    # 打印当前目录下的文件
    print("Files:")
    for filename in filenames:
        print(f" - {filename}")

    # 打印一个分隔线
    print("-" * 40)

运行这个代码会产生如下输出:

Current Directory: my_folder
Subdirectories:
 - dir1
 - dir2
Files:
 - file6.txt
----------------------------------------
Current Directory: my_folder\dir1
Subdirectories:
Files:
 - file1.txt
 - file2.txt
----------------------------------------
Current Directory: my_folder\dir2
Subdirectories:
 - sub_dir
Files:
 - file5.txt
----------------------------------------
Current Directory: my_folder\dir2\sub_dir
Subdirectories:
Files:
 - file3.txt
 - file4.txt
----------------------------------------

8.5 shutil 模块(移动、复制、压缩文件)

参考《shutil — 高阶文件操作》

  shutil 模块是Python标准库中的一个强大工具,用于文件和目录操作。它提供了许多函数,可用于复制、移动、删除文件和目录,以及执行各种文件操作任务,以下是其主要用法:

方法描述示例
shutil.copy(src, dst)复制文件从源路径到目标路径。shutil.copy('source.txt', 'destination.txt')
shutil.move(src, dst)移动文件或重命名文件。shutil.move('old_name.txt', 'new_name.txt')
shutil.copytree(src, dst)递归复制整个文件夹及其内容。shutil.copytree('source_folder', 'destination_folder')
shutil.rmtree(path)递归删除整个目录树,包括所有文件和子目录。shutil.rmtree('folder_to_delete')
shutil.make_archive(base_name, format)创建归档文件,例如 ZIP 文件。hutil.make_archive(archive_name, format='zip', root_dir='folder_to_compress')
shutil.unpack_archive(filename, extract_dir)解压缩归档文件。shutil.unpack_archive('my_archive.zip', 'extracted_folder')

归档函数语法为:

shutil.make_archive(base_name, format, root_dir=None, base_dir=None)

参数含义:

  • base_name:归档文件的基本名称,不包括文件扩展名。例如,如果设置为 'my_archive',则将创建 'my_archive.zip''my_archive.tar.gz' 等文件。
  • format:归档文件的格式,通常是字符串,可以是 'zip''tar''gztar''bztar''xztar' 等。
    ‘gztar’:gzip 压缩的 tar 归档文件。
    ‘bztar’:bzip2 压缩的 tar 归档文件。
    ‘xztar’:xz 压缩的 tar 归档文件
  • root_dir(可选):要归档的根目录,如果不提供,默认为当前工作目录。
  • base_dir(可选):要归档的基本目录,如果提供,将在 root_dir 下创建一个子目录,并将其内容归档。

tar是未压缩的tar 归档文件,后三者分别是gzip格式、bzip2格式和xz格式压缩的tar文件。

下面是一个示例假设有以下目录结构:

my_project/
    ├── source/
    │     ├── file1.txt
    │     ├── file2.txt
    │     └── subfolder/
    │         └── file3.txt
    └── other_folder/
          └── file4.txt

现在用zip格式只归档source和other_folder目录到指定目录下,代码可以写为:

import shutil

# 源目录的根目录
source_root_dir = 'my_project/'

# 要归档的子目录列表
subdirectories_to_archive = ['source/', 'other_folder/']

# 目标位置
destination_dir = 'destination_directory/'

# 归档文件的名称和格式
archive_name = 'my_archive'
format = 'zip'

# 创建 ZIP 归档文件,指定 root_dir 为源目录,base_dir 为子目录列表
shutil.make_archive(
    archive_name, format, root_dir=source_root_dir, base_dir=subdirectories_to_archive
)

# 将归档文件移动到目标位置
shutil.move(f'{archive_name}.{format}', destination_dir) 

# 解压到source目录下
shutil.unpack_archive(archive_file, 'my_project/source/')

8.6 Python数据的序列化

Pyhon官方文档:json模块、pickle模块

8.6.1 数据的序列化和反序列化

  前者介绍的read、write这些方法,都是进行字符串的读取,即使是数字,也是转成字符串格式,而如果碰到嵌套列表和字典等更复杂的数据类型时,手动解析将会变得非常复杂。这就涉及到数据的序列化和反序列化:

  • 序列化(Serialization)
      将数据从其原始数据结构(通常是Python对象)转换为一种可以在不同程序、不同计算机或不同时间之间传输或存储的格式的过程,通常转换为文本或二进制表示,以便能够保存到文件、发送到网络、存储在数据库中或在其他情况下传输和使用。
      在Python中,常见的序列化方法包括使用JSON(JavaScript Object Notation)或Pickle(Python特定的序列化格式)等。

  • 反序列化(Deserialization):将序列化后的数据重新还原为可以在程序中操作的Python对象,用于在程序中进一步处理数据。。
      python提供了json标准库,可以使用 JSON (JavaScript Object Notation)格式进行数据的交换。JSON数据是纯文本,易于阅读和编写,也易于解析和生成,非常适合处理复杂结构的数据,在Web开发、API通信以及配置文件中广泛使用。

8.6.2 json模块

  Python的json模块允许你序列化Python对象(将其转换为JSON格式)和反序列化JSON数据(将JSON数据转换为Python对象),其主要方法有:

json方法描述
json.dumps(obj)将Python对象序列化为JSON字符串
json.loads(json_str)将JSON字符串反序列化为Python对象
json.dump(obj, file)将Python对象序列化为JSON并保存到文件中
json.load(file)从文件中加载JSON数据并反序列化为Python对象

下面是示例代码:

# 将Python对象转换为JSON
import json

data = {
    "name": "John",
    "age": 30,
    "city": "New York"
}

# 将Python字典转换为JSON字符串
json_data = json.dumps(data)

# 保存JSON字符串到文件
with open("data.json", "w") as json_file:
    json.dump(data, json_file)
import json
# 从JSON字符串反序列化为Python字典
json_data = '{"name": "John", "age": 30, "city": "New York"}'
data = json.loads(json_data)

# 从文件中加载JSON数据
with open("data.json", "r") as json_file:
    data = json.load(json_file)
8.6.3 pickle模块

  pickle是Python的一种序列化模块,它能够将Python对象转换为二进制格式,所以它可以序列化几乎所有Python对象,包括自定义类的实例和函数、模型等。以下是其常用的方法:

pickle模块方法描述
pickle.dumps(obj[, protocol])将Python对象序列化为包含二进制数据的字符串
pickle.loads(bytes_object)从包含二进制数据的字符串中反序列化为Python对象
pickle.dump(obj, file[, protocol])将Python对象序列化为二进制数据并保存到文件中
pickle.load(file)从文件中加载二进制数据并反序列化为Python对象
pickle.Pickler(file[, protocol])创建一个Pickler对象,用于将对象序列化到文件或文件类中
pickle.Unpickler(file)创建一个Unpickler对象,用于从文件或文件类中反序列化对象
pickle.HIGHEST_PROTOCOL常量,表示pickle使用的最高协议版本(通常是最新的)

  上述列表中,可选参数protocol用于指定序列化使用的协议版本,默认为最高版本。列表列举出三种序列化和反序列化方法,区别如下:

  • pickle.dump:用于将Python对象序列化为二进制数据并保存到文件中。
  • pickle.dumps:用于将Python对象序列化为包含二进制数据的字符串(不保存到文件)。
  • pickle.Pickler:创建一个Pickler对象,可用于处理多个Python对象。

下面举例说明:

import pickle

# 示例数据
data1 = {
    "name": "Alice",
    "age": 25,
    "city": "London"
}

data2 = {
    "name": "Bob",
    "age": 30,
    "city": "New York"
}

# 使用 pickle.dump 进行序列化,结果保存在文件中
with open("data.pkl", "wb") as pickle_file:
    pickle.dump(data1, pickle_file)
    
# 使用 pickle.load 读取文件,进行对应的反序列化
with open("data.pkl", "rb") as pickle_file:
    loaded_data1 = pickle.load(pickle_file)
    
'---------------------------------------------------------------'

# 使用 pickle.dumps 进行序列化,结果赋值给一个变量
pickle_data = pickle.dumps(data2)

# 使用 pickle.loads 反序列化这个变量
loaded_data2 = pickle.loads(pickle_data)

'---------------------------------------------------------------'

# 使用 pickle.Pickler 和 pickle.Unpickler 进行序列化和反序列化
with open("data.pkl", "wb") as pickle_file:
    pickler = pickle.Pickler(pickle_file)      
    pickler.dump(data1)    									# 序列化数据1
    pickler.dump(data2)										# 序列化数据2

with open("data.pkl", "rb") as pickle_file:
    unpickler = pickle.Unpickler(pickle_file)       
    loaded_data1_pickled = unpickler.load()					# 反序列化数据1   
    loaded_data2_pickled = unpickler.load()					# 反序列化数据2

# 打印反序列化后的数据
print("Loaded Data 1:", loaded_data1)
print("Loaded Data 2:", loaded_data2)
print("Loaded Data 1 (Pickler):", loaded_data1_pickled)
print("Loaded Data 2 (Pickler):", loaded_data2_pickled)

  Pickler 对象可以将多个序列化的对象存储在同一个文件中,Unpickler 对象在加载时会按顺序逐个读取和反序列化这些对象,所以上述代码中,会多次调用 unpickler.load() 来逐个加载这些对象,并将它们分配给不同的变量。

总结: json和pickle的区别

  1. JSON(JavaScript Object Notation)

    • 用于文本序列化格式,适用于序列化简单的数据结构,如字典、列表、字符串和数字,人类更容易解读。
    • 通用性强,可以在不同编程语言之间轻松交换数据。
    • 安全性高,对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞。
  2. Pickle

    • Python特定的二进制序列化格式,可以用于序列化几乎所有Python对象,包括自定义类的实例和函数。
    • pickle序列化是Python专用的,不适合与其他编程语言交互
    • pickle可以加载包含恶意代码的数据,所以需要谨慎使用(只在信任的源中使用pickle)。
8.6.4 示例:使用pickle保存lightGBM模型&加载预测

见我的另一篇帖子《lightGBM实战》

在Windows 10系统的命令提示符(CMD)中,要切换到D:\VM17\x64目录,你可以使用以下命令:

cd /d D:\VM17\x64

这个命令中:

  • cd 是用来改变当前目录的命令。
  • /d 选项用于切换到指定的驱动器(在这种情况下是D:\),即使当前工作目录在不同的驱动器上。
  • D:\VM17\x64 是你要切换到的目标目录的路径。

本文标签: 文件系统模块教程pipConda