Python3.11 Tutorial 3: Modules and packages (pip/conda), file system (os/ shutil/json/pickle/openpyxl/xlrd)

Links related to this article:

7. Modules and packages

  In Python, modules and packages are important ways to organize and manage code. They help split code into manageable chunks, improving code reusability and maintainability. Now, let’s understand these two concepts in depth.

7.1 Modules

  A module is a file containing Python code, and its file name is the module name plus a suffix .py. Files can contain functions, variables, and classes that are organized together for reuse elsewhere. So by simply writing Python code and saving it as a .py file, we create a module that other programs can import and use the functionality within.

  The file name of the module should be a valid Python identifier, follow the Python naming convention, and cannot have the same name as Python keywords or the standard library or installed third-party libraries to prevent conflicts. It's best to use descriptive variable, function, and class names to improve code readability.

7.1.1 Module search path

  In Python, to use content from a module or package, you need to import them. The Python interpreter searches the module's path when importing it, including the following locations:

  • Current directory
  • Where to install the Python standard library
  • PYTHONPATHThe directory specified in the environment variable
  • Some default directories built into Python

  These paths are searched in order of priority. First, the interpreter looks to see if the required module is present in the current directory, before continuing to search other paths. If a matching module is found, the interpreter imports it.

  The standard library is usually installed in the Python installation directory, and the site-packages directory contains the installation locations of third-party modules. Run the following code and you will see the installation location of the Python standard library and the path to the site-packages directory.

import sys
import site

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

print("\nPython的site-packages目录:")
print(site.getsitepackages())

7.1.2 PYTHONPATHandsys.path

  1. PYTHONPATH

  Environment variables PYTHONPATHallow you to customize the module search path, which is useful for using custom modules or third-party modules in your project. You can use the following methods to view and set PYTHONPATH.

  • echo $PYTHONPATHOn Linux or macOS systems, you can execute commands in the terminal to view PYTHONPATH, use export PYTHONPATH=/path/to/your/custom/modulesto specify the search path for custom modules in PYTHONPATH.

  • In windows system, you can use Python code to access and configure 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未设置")
    

    The above code changes the value of PYTHONPATH only in the current Python process. If you want to change the system environment variable permanently, you need to add it in the system environment variable.

  1. sys.path

  sys.pathis a list containing directory paths that the Python interpreter searches when importing a module. The following is sys.pathan example of how to use the management module search 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.pathPYTHONPATHThe difference between

  sys.pathand PYTHONPATHare both used to manage the search path for Python modules, but there are some important differences between them:

  1. Scope of action :

    • sys.pathIs Python interpreter level, it only affects the module search path in the current Python process. This means that sys.pathchanges to will only take effect within the current Python program and will not affect the global environment or other Python processes.

    • PYTHONPATHis an operating system-level environment variable that affects the module search path of all Python processes, including Python scripts you run on the command line, code in the Python IDE, and all other Python programs. Therefore, changes PYTHONPATHcan globally affect the search path for Python modules.

  2. Priority :

    • When importing a module, sys.pathpaths in take precedence over PYTHONPATHpaths in . This means that if sys.patha matching module is found in , it will be used first, without looking for PYTHONPATHpaths in .

    • If sys.paththe module is not found in , the Python interpreter will continue looking for PYTHONPATHpaths in .

    • If a virtual environment is used, the virtual environment is automatically managed sys.pathso that modules installed in the virtual environment take precedence over global modules. This ensures that your project's dependencies are well isolated and managed.

  3. Dynamic :

    • You can use Python code modification at runtime sys.pathto dynamically add or remove module search paths without restarting the Python process.

    • To change PYTHONPATH, you usually need to restart the Python process associated with the environment variable, which means the change may need to take effect before starting a new process.

  4. Locality :

    • sys.pathMore suitable for project-level management, you can use it in the project code to manage module search paths to ensure that the project's dependencies are well isolated.

    • PYTHONPATHMore suitable for global configuration, used to set system-wide module search paths, which may affect multiple projects and Python processes.

  In summary, sys.pathit is more flexible and suitable for managing module search paths at the project level without affecting the global environment. Whereas PYTHONPATHfor global configuration, affects all Python processes. Typically, it sys.pathis more common to use virtual environments in projects as it provides better isolation and control, which is useful for using different module versions or custom modules in different projects.

7.1.2 Module import and common errors

Python provides different module import methods to meet different needs.

  • Use to import module_nameimport the entire module.
  • Use to from module_name import function_nameimport a specific function or variable in a module.
  • Use asthe keyword to create aliases for modules or imported content.

Example:

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数组

In addition to using a complete absolute path or module name, you can also use a relative path to import modules, which can be divided into two cases:

  1. Relative import relative to the current module: This method allows you to make relative imports between modules in the same directory.

Let's say you have the following project structure:

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

If you want to import module1.pyfrom module2.py, you can import using a relative path as follows:

# module1.py

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

We generally use to .represent the current directory and use ..to represent the parent directory.

  1. Importing relative to a top-level package
    If your project is a package (a __init__.pydirectory containing a file), you can import using a relative path relative to the top-level package.

Assume you have the following project structure:

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

If you want to import module1.pyfrom module2.py, you can use relative path import as follows:

# module1.py

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

  In this example, ..represents the parent package ( my_package), which then imports the child subpackagepackage module2.py.

  It should be noted that relative path imports are usually used inside packages to import between modules within the package, which can make the project structure clearer, but need to be used with caution to ensure the correctness of relative paths. It is usually more common to import using an absolute path or directly using the module name.

7.1.3 Module caching mechanism

For more details and decision-making flow chart of this part, see PEP 3147

  In order to quickly load modules, the Python interpreter caches already imported modules to avoid repeated imports and improve performance. This means that the module will only be executed once the first time it is imported, subsequent imports will use the cached version.

  Python generally caches the compiled version of the module in __pycache__the directory. The file name is module.version.pyc, in which the version number of the compiler is specified. For example, in the 3.3 release of CPython, spam.pythe compiled version cache is __pycache__/spam.cpython-33.pyc. This naming convention allows modules compiled with different Python versions to coexist.

  In addition, Python will compare the modification date of the compiled version and the source code to see whether the compiled version has expired and whether it needs to be recompiled. This process is completely automatic and has nothing to do with the platform. Therefore, Python can be shared between systems with different architectures. Same library.

7.1.4 __name__and __main__functions

  __name__is a built-in special variable whose value is the name of the module when the module is imported __name__. But when the module is run directly, __name__the value is "__main__", so it can be used to determine whether the module is run as the main program or is imported into another program.

  The code we __main__write in the function is only executed when the module is run directly, but not when it is imported. Let's say we have a my_module.pymodule called with the following code:

# 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.")

  Now, if we import this module in another script, only elsethe branch will be executed, since __name__the value of is the name of the module:

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

But if we run this module directly, if __name__ == "__main__":the code block below it will be executed:

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

This allows us to include some code for testing or execution in the module without having to worry about it being executed on import.

7.2 Standard library

For details, see the official documentation of the Python standard library

  A library is a collection of related modules, usually with a specific domain or functionality. Libraries can also be called packages. The Standard Library (Standard Library) is a set of modules and functions built into Python, the core part of the Python ecosystem. These modules and functions provide standard solutions for various common tasks and operations. Windows version of Python installers usually include the entire standard library, and often many additional components. Please refer to the official documentation for details.

7.3 Packages

  A package is a directory containing multiple modules. It allows related modules to be grouped together into a hierarchy. Packages help to better organize code for large projects. They also avoid naming conflicts, since modules inside each package have their own namespace.

7.3.1 Creating packages

  Creating a package simply creates a __init__.pydirectory containing the files and places the module files in it. This __init__.pyfile can be empty, but it indicates that this is a package. The following describes __init__.pythe function of the file in detail.

  • Identify a directory as a package

    If you want to treat a directory as a Python package, then the directory must contain a __init__.pyfile named , and Python will use this file to determine whether the directory should be treated as a package.

  • Prevent directory name and module name conflicts.

    For example, without __init__.pya file, Python might stringmistake a directory called for a package, causing problems in subsequent searches for modules.

  • Initialize some features of the package

    __init__.pyPython code can also be included, which can be initialization code that is automatically executed when the package is imported, or that defines the public interface of the package. For example, you can set special variables __all__, which are mainly used to control the public interface of the module, specifying from package import *which properties, functions, classes, etc. in one module or package should be imported into other modules when using the statement. The following is __all__a detailed introduction about:

  1. Function :

    • __all__The purpose of is to limit from module import *what is imported via the statement to avoid importing unnecessary identifiers, while allowing developers to explicitly specify which identifiers should be the public interface of the module.
  2. How to use :

    • __all__is a List containing strings, each of which is the name of an identifier defined inside the module.

    • At the top level of a module, you can define __all__variables, which explicitly specify from module import *identifiers that can be imported in statements. Suppose there is a module my_module.pycontaining the following:

      def public_function():
          pass
      
      def _private_function():
          pass
      
      class PublicClass:
          pass
      
      class _PrivateClass:
          pass
      
      __all__ = ['public_function', 'PublicClass']
      
    • If from my_module import *the statement is used, only public_functionand PublicClasswill be imported, not _private_functionand ._PrivateClass

    • If used from my_module import _private_function, it can be imported _private_functionsince it __all__is not listed in .

  3. Note :

    • __all__It is not a mandatory rule of Python, but a convention. It is mainly used to guide other developers on what should be imported when using the module.

    • Identifiers starting with a single underscore _are generally considered private and should not be imported directly, but may still be imported if needed.

  In short, __all__it is a way to control the public interface of a module or package, which helps improve the maintainability and readability of the code while avoiding the import of unnecessary identifiers.

7.3.2 Import package

  We also use the import statement and specify the package name and module name to import the modules in the package. Packages can form a hierarchical structure, that is, contain sub-packages and modules under sub-packages. Importing can also be completed by specifying the complete path. A common project structure might look like this:

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

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

  In actual use, try to avoid using it from module import *, it is better to explicitly import the required identifier, for example from module import func1, class1, because this will cause two problems:

  1. Namespace Pollution :

    • When you use from module import *, Python will import all identifiers in the module into the current namespace, including identifiers in the module that may not have been explicitly designed as public interfaces. This means that your namespace will become very crowded, filled with many identifiers that you may not need, that is, namespace pollution.
    • These additional identifiers may conflict with identifiers already defined in your code, causing unpredictable behavior or errors.
  2. The code is difficult to understand :

    • When you see a lot of statements in your code from module import *, it can be difficult to determine which identifiers actually come from the module because they are not explicitly listed. This makes the code difficult to understand and maintain because you can't quickly identify which identifiers come from which module.
    • At the same time, if you use multiple from module import * statements, identifiers in different modules may overwrite each other, further increasing the possibility of confusion and errors.

7.3.3 pip package manager

  pip is a Python package manager dedicated to installing, upgrading and managing Python packages. The following are commonly used pip commands:

pip command describe
pip --version Verify that pip is installed and display the version number
pip list List all installed packages and their version numbers
pip install --upgrade pip Upgrade pip to the latest version
pip install package_name Installation package
pip install package_name==desired_version Install a specific version of a package
pip install --upgrade package_name Upgrade specified package
pip uninstall package_name Uninstall specified package
pip freeze > requirements.txt Save the package list of the current environment to the requirements.txt file
pip install -r requirements.txt Install all dependencies from requirements file
pip show package_name Displays detailed information about the specified package, including version and installation path
Virtual environment related commands describe
pip install virtualenv Install the virtual environment module (if needed)
python -m venv myenv Create a new virtual environment (myenv is the name of the virtual environment)
myenv\Scripts\activate(Windows) Activate the virtual environment (on Windows)
source myenv/bin/activate(macOS and Linux) Activate the virtual environment (on macOS and Linux)
deactivate Exit the virtual environment
Other commands (not commonly used) describe
pip check Check if there is a problem with the package in the current environment
pip search query Search for packages containing the specified keyword
pip download package_name Download the source code for the specified package, but do not install it
pip install --user package_name Install packages at user level, not global or virtual environment
pip install --no-cache-dir package_name Disable caching when installing packages to solve some installation problems

7.3.4 cond

Miniconda , Anaconda , conda documentation

  venv is a virtual environment management tool that comes with Python's standard library and only contains necessary components related to the Python environment. This makes it suitable for creating pure Python environments, i.e. packages that do not support non-Python languages. Therefore, conda is generally used to manage virtual environments.

  conda is a cross-platform virtual environment manager that not only supports Python, but also supports other programming languages ​​and can also manage system dependencies. There are currently two versions - Miniconda and Anaconda . The former is only a few hundred M and is a minimized version that only includes Conda and Python. Users can customize other packages that need to be installed. The latter has 5 or 6G and comes with many pre-installed packages suitable for various data science tasks.

Here are some commonly used conda commands:

conda package management commands describe
conda create --name myenv python=3.8 Create a virtual environment named myenv and specify the python version as 3.8
conda activate myenv
source activate myenv
Activate a virtual environment (windows)
Activate a virtual environment (macOS and Linux)
conda install package_name Install Python packages in the activated virtual environment
conda list List installed packages in the current virtual environment
conda deactivate Deactivate the current virtual environment
conda env export > environment.yml Export the current virtual environment configuration to a YAML file
conda env create -f environment.yml Create a virtual environment based on a YAML file
conda remove --name myenv --all Delete the virtual environment with the specified name and all its packages
conda search package_name Search for packages available for installation
conda update --all Upgrade all packages in the current virtual environment

when running

conda virtual environment management command describe
conda update conda Upgrade conda itself
conda config --show Display conda configuration information
conda env list List all created virtual environments
conda info --env Display details of the current virtual environment
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.org
"""

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. 自动化构建和测试:

  • 编写单元测试来验证模块和函数的正确性。这有助于捕获潜在的错误,并确保代码在修改后仍然正常工作。
  • 使用测试框架(如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())
col1 col2 col3 col4 col5
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
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实战》

Guess you like

Origin blog.csdn.net/qq_56591814/article/details/132643285