Python学习历程——模块

Python学习历程------模块

  • 注意事项
  • 一、模块的核心概念
    • [1.1 什么是模块](#1.1 什么是模块)
    • [1.2 为什么使用模块](#1.2 为什么使用模块)
    • [1.3 模块 vs 脚本](#1.3 模块 vs 脚本)
  • 二、模块的导入与使用
    • [2.1 import module_name](#2.1 import module_name)
    • [2.2 from module_name import name1, name2](#2.2 from module_name import name1, name2)
    • [2.3 from module_name import *](#2.3 from module_name import *)
    • [2.4 import module_name as alias (使用别名)](#2.4 import module_name as alias (使用别名))
  • 三、创建与组织模块
    • [3.1 创建一个简单的模块](#3.1 创建一个简单的模块)
    • [3.2 包(Package)的概念与创建](#3.2 包(Package)的概念与创建)
    • [3.3 Python的模块搜索路径 (sys.path)](#3.3 Python的模块搜索路径 (sys.path))
    • [3.4 绝对导入与相对导入](#3.4 绝对导入与相对导入)
      • [1. 绝对导入](#1. 绝对导入)
      • [2. 相对导入](#2. 相对导入)
    • [3.5 如何添加自定义模块路径](#3.5 如何添加自定义模块路径)
      • [1. 在运行时动态修改 sys.path (临时方案)](#1. 在运行时动态修改 sys.path (临时方案))
      • [2. 设置 PYTHONPATH 环境变量 (项目/用户级方案)](#2. 设置 PYTHONPATH 环境变量 (项目/用户级方案))
      • [3. 创建可安装的包](#3. 创建可安装的包)
    • [4.1 内置模块 (Built-in Modules)](#4.1 内置模块 (Built-in Modules))
    • [4.2 标准库 (The Standard Library)](#4.2 标准库 (The Standard Library))
    • [4.3 第三方模块与PyPI](#4.3 第三方模块与PyPI)
    • [4.4 使用pip管理包](#4.4 使用pip管理包)
    • [4.5 虚拟环境](#4.5 虚拟环境)
  • 第五部分:高级主题与最佳实践 (Advanced Topics & Best Practices)
    • [5.1 if name == 'main': 的作用与重要性](#5.1 if name == 'main': 的作用与重要性)
    • [5.2 模块的命名空间与作用域](#5.2 模块的命名空间与作用域)
    • [5.3 动态导入 (importlib)](#5.3 动态导入 (importlib))
    • [5.4 模块的重载 (importlib.reload)](#5.4 模块的重载 (importlib.reload))

注意事项

💡 本文还是和之前的风格一致,最贴合Java开发人员,重点知识点会详细讲解。

💡 啊哈哈,要是觉得可以那就点点赞和收藏吧。

一、模块的核心概念

1.1 什么是模块

💡 直接定义 :模块就是.py.结尾的文件。

💡 模块的命名空间:当我们使用import导入某个模块时,只是在当前文件增加了一个引用,而不是将代码都加载到当前文件。

python 复制代码
# greetings.py
MESSAGE = "Hello, World!"

def say_hello(name):
    return f"Hello, {name}!"

Python中的模块和TypeScript中的模块(也可以称为hock)很像,可以定义多个常量或方法的模块,但在Java中更多的被称为工具类,虽然从使用上讲这是一样的作用。

1.2 为什么使用模块

💡 不要忽视这些细小的知识点 ,这是考验我们是否专业的重要方向,使用模块的理由如下:

  1. 代码组织和维护
  2. 代码重用
  3. 命名空间隔离(重要!)

1.3 模块 vs 脚本

💡 要区分模块和脚本是通过我们的使用意图来决定的。

  • 脚本:一个你打算直接运行的文件,一般使用在程序的入口。
  • 模块:一个你打算被其他程序导入(import)的文件,目的是为了提供可重用的功能(函数、类等)。

在Java中我们习惯了使用psvm创建一个main方法,这个方法就是程序的主入口,一般只有一个,也只有这个main方法才能启动程序,而Python中什么文件都可以直接运行,最终执行的都是类似C:\Python310\python.exe "D:\MyProjects\my_app\main.py"的命令而已,只不过我们约定,脚本中需要包含类似下面的语句,当然你不写同样可以运行,因为代码的运行就是从上到下。

python 复制代码
if __name__ == "main":
    d.ping()

注意变量__name__是一个魔法变量,由编译器修改,我们不能修改这个变量

二、模块的导入与使用

一共四种导入方式,各有利弊

python 复制代码
# mymath.py
PI = 3.14159

def area_of_circle(radius):
    """计算圆的面积"""
    return PI * (radius ** 2)

def factorial(n):
    """计算阶乘"""
    if n < 0:
        return "输入必须为非负数"
    elif n == 0:
        return 1
    else:
        # 这是一个简单的递归实现
        return n * factorial(n - 1)

2.1 import module_name

💡 这是最标准、最安全的导入方式。

python 复制代码
import mymath

radius = 10
# 必须使用 mymath. 前缀
area = mymath.area_of_circle(radius)

print(f"PI 的值是: {mymath.PI}")
print(f"半径为 {radius} 的圆面积是: {area}")
print(f"5的阶乘是: {mymath.factorial(5)}")

2.2 from module_name import name1, name2

💡 当你只需要模块中的一两个成员时,这种方式很方便,一般这种是最常用的。这也是Java中最常用的,往往我们只需要导入其中一两个。

python 复制代码
from mymath import area_of_circle, PI

# 注意:这里不能使用 factorial,因为它没有被导入
# print(factorial(5))  # 这行会报错 NameError: name 'factorial' is not defined

radius = 10
# 直接使用,无需前缀
area = area_of_circle(radius)

print(f"PI 的值是: {PI}")
print(f"半径为 {radius} 的圆面积是: {area}")

2.3 from module_name import *

⚠️ 非必要不要使用这种,会污染全局作用域,除非你要使用这个模块的所有内容

2.4 import module_name as alias (使用别名)

💡 当模块名太长,或者为了遵循广泛接受的社区惯例时,这是最佳选择。

python 复制代码
import mymath as mm

radius = 10
area = mm.area_of_circle(radius)

print(f"PI 的值是: {mm.PI}")
print(f"半径为 {radius} 的圆面积是: {area}")

三、创建与组织模块

3.1 创建一个简单的模块

python 复制代码
# string_utils.py

AUTHOR = "AI Assistant"

def count_vowels(input_string):
    """统计字符串中元音字母的数量。"""
    count = 0
    vowels = "aeiouAEIOU"
    for char in input_string:
        if char in vowels:
            count += 1
    return count

def reverse_string(input_string):
    """反转一个字符串。"""
    return input_string[::-1]
python 复制代码
# main.py

# 导入整个 string_utils 模块
import string_utils

my_name = "Gemini"

# 使用 "模块名.成员名" 的方式访问模块内容
reversed_name = string_utils.reverse_string(my_name)
vowel_count = string_utils.count_vowels(my_name)

print(f"Original name: {my_name}")
print(f"Reversed name: {reversed_name}")
print(f"Number of vowels: {vowel_count}")
print(f"This utility was created by: {string_utils.AUTHOR}")

这里就不多说了,比较简单的创建和导入使用。

3.2 包(Package)的概念与创建

在Python中包的概念就是文件夹,只是和Java不同的是,它需要一个__init__文件来说明,这个文件中不需要写任何内容,只是表明这是一个包,之后在我们使用的时候就通过包名.模块进行导入。

python 复制代码
my_project/
├── main.py
└── ecommerce/              <-- 这是我们的包
    ├── __init__.py         <-- 关键文件,让 'ecommerce' 成为一个包
    ├── products.py
    └── payments.py
python 复制代码
# main.py (位于 my_project/ 目录下)

# 使用 "包名.模块名" 的方式导入
import ecommerce.products
import ecommerce.payments

# 调用时也需要完整的路径
print(ecommerce.products.get_product_details("XYZ-001"))
print(ecommerce.payments.process_payment("A-12345", 99.99))

# 也可以使用 from...import 语法
from ecommerce.products import get_product_details

print(get_product_details("ABC-789"))

3.3 Python的模块搜索路径 (sys.path)

sys是一个内置模块,path是其中的一个变量,包含了当前系统的一个路径列表,也就是我们的导入查询的顺序关系,一般我们可以通过以下的写法进行查看。

python 复制代码
import sys
import pprint
pprint.pprint(sys.path)

这个顺序非常重要,模块就是在这个列表中进行寻找的,一旦找到了一个那就结束了,所以如果出现多个相同名称的模块,就要看在这里列表中哪个路径优先了。

3.4 绝对导入与相对导入

1. 绝对导入

python 复制代码
# ecommerce/payments.py

# 从项目的根 'ecommerce' 开始写起
from ecommerce.products import get_product_price

def process_payment(order_id, product_id):
    price = get_product_price(product_id)
    return f"Processing payment of ${price} for order {order_id}."

优点:无论你把 payments.py 移动到包内的哪个位置,只要项目结构不变,这条导入语句永远有效。非常明确和稳定。

2. 相对导入

python 复制代码
# ecommerce/payments.py

# '.' 代表当前目录 (ecommerce/),从这里导入 products 模块
from .products import get_product_price

def process_payment(order_id, product_id):
    price = get_product_price(product_id)
    return f"Processing payment of ${price} for order {order_id}."

优点:如果整个 ecommerce 包被重命名(比如改成 shopping),这条导入语句不需要修改,因为它只关心内部的相对位置。

重要限制 :你不能直接运行一个使用了相对导入的文件。如果你尝试 python ecommerce/payments.py,会得到 ImportError: attempted relative import with no known parent package。因为Python不知道它的"父包"是谁。这类文件必须作为模块被一个顶层脚本(如main.py)导入和执行。

3.5 如何添加自定义模块路径

1. 在运行时动态修改 sys.path (临时方案)

python 复制代码
import sys

# 假设你的工具模块在 '../libs' 目录下
# 最好使用绝对路径来避免混淆
import os
# __file__ 是当前文件的路径
# os.path.dirname() 获取该路径的目录
# os.path.abspath() 转换为绝对路径
current_dir = os.path.dirname(os.path.abspath(__file__))
libs_path = os.path.join(current_dir, '..', 'libs')

# 将路径添加到 sys.path
if libs_path not in sys.path:
    sys.path.append(libs_path)

import my_utility_module # 现在可以成功导入了

注意,这只是临时解决方案,会出现大量的重复代码。

2. 设置 PYTHONPATH 环境变量 (项目/用户级方案)

可以在操作系统层面设置这个环境变量,指向你的模块库目录。

  • Windows: set PYTHONPATH=C:\path\to\your\libs
  • Linux/macOS: export PYTHONPATH=/path/to/your/libs

优点: 一次设置,该用户的所有Python脚本都能受益。适合个人开发环境或特定的项目环境配置。

缺点: 部署时容易忘记配置,导致代码在另一台机器上无法运行。不方便进行项目间的隔离。

这是项目中常用的手段,但还是不够优雅。

3. 创建可安装的包

这里可以mark一下,后面简单说说,原理类似于Java、Vue中提出一个单独的模块,然后使用jar包和npm包导入到项目中,使用起来较为复杂,当然也可以将其发布到官方的库里面,直接通过依赖引入,也可以构建自己的npm库、Maven库等,这都是一个道理。

第四部分:内置模块、标准库与第三方模块 (Built-in, Standard & Third-Party Modules)

4.1 内置模块 (Built-in Modules)

Python内置的一些模块,类似Java的ArrayList、HashMap等等。

python 复制代码
import sys

# sys.builtin_module_names 是一个包含所有内置模块名称的元组
print(sys.builtin_module_names)

# ('_abc', '_ast', '_codecs', '_collections', '_functools', 
#  '_imp', '_io', '_locale', '_operator', '_signal', 
#  '_sre', '_stat', '_string', '_thread', '_tracemalloc', 
#  '_warnings', '_weakref', 'atexit', 'builtins', 'errno', 
#  'faulthandler', 'gc', 'itertools', 'marshal', 'posix', 
#  'pwd', 'sys', 'time', 'xxsubtype', 'zipimport') 
# (输出可能因操作系统和Python版本略有不同)

4.2 标准库 (The Standard Library)

可以理解为Maven库,只不过Maven库中会存在大量其它机构发布的项目,而Python的标准库大多数都是其官方提供的,功能强大,安全有保障。

标准库里有什么

  • 文本处理: string, re (正则表达式)
  • 数据结构: collections (提供Counter, defaultdict等), datetime (处理日期和时间)
  • 数学运算: math, random, statistics
  • 文件与目录: os, pathlib (现代化的路径操作), glob
  • 数据持久化与交换: json, csv, pickle, sqlite3
  • 网络编程: socket, http.client, urllib
  • 并发编程: threading, multiprocessing, asyncio

4.3 第三方模块与PyPI

这才是第三方集成的一个库,里面功能同样很强大,类型相对要多一些。

网址:pypi.org

4.4 使用pip管理包

和node的npm一样,同样是一个安装的命令。

核心pip命令:

  • 安装包:

    • pip install <package_name>
    • 例如,安装强大的HTTP请求库 requests:
    • pip install requests
  • 卸载包:

    • pip uninstall <package_name>
    • pip uninstall requests
  • 查看已安装的包:

    • pip list
    • 会列出当前环境中所有已安装的包及其版本。
  • 升级包:

    • pip install --upgrade <package_name>
    • pip install --upgrade requests

4.5 虚拟环境

💡 Python有一个非常强大的特性,就是可以在项目中创建虚拟环境,不同项目中可以使用不同版本的依赖,从而达到隔离的效果,非常有用。

生成依赖文件:

pip freeze > requirements.txt

这会创建一个名为 requirements.txt 的文件,里面包含了 pip list 的所有内容,格式为 package_name==version。

根据文件安装依赖

pip install -r requirements.txt

虚拟环境

这里需要大家手动尝试,这对于安装了不同版本的Python非常的有用,可以直接安装对应版本的依赖,然后用对应版本的Python命令启动环境。

python 复制代码
# 1. 在你的项目文件夹下,创建一个名为 venv 的虚拟环境
python -m venv venv

# 2. 激活环境
# On Windows
# venv\Scripts\activate
# On macOS/Linux
# source venv/bin/activate

# 激活后,你的终端提示符前会出现 (venv)
# (venv) C:\Users\YourUser\my_project> 

# 3. 在此环境中用 pip 安装的包,将只存在于这个环境,不会影响全局
# (venv) pip install requests
# (venv) pip freeze > requirements.txt

# 4. 完成工作后,退出环境
# (venv) deactivate

第五部分:高级主题与最佳实践 (Advanced Topics & Best Practices)

5.1 if name == 'main': 的作用与重要性

💡 if __name__ == '__main__': 是一个条件判断语句,它所包含的代码块只会在该Python文件被直接运行时执行,而不会在该文件作为模块被其他文件import时执行。它的核心作用是区分一个文件是作为可执行脚本还是作为可导入的库,从而将"测试代码"或"脚本入口"与"库功能代码"分离开。

python 复制代码
def cool_function():
    print("Executing cool_function()")

print(f"The __name__ of my_utility.py is: {__name__}")

# 这是一个脚本入口或测试区
if __name__ == '__main__':
    print("my_utility.py is being run directly.")
    cool_function()

直接运行这个文件会输出三段。

python 复制代码
import my_utility

print(f"\nNow running main.py")
print("Calling function from the imported utility...")
my_utility.cool_function()

运行上述文件,不会执行 print("my_utility.py is being run directly.")

cool_function(),因为此时__name__不是main。

5.2 模块的命名空间与作用域

💡 每个Python模块都有其自己独立的全局命名空间(Global Namespace)。当你import my_module时,你并没有将my_module中的所有变量和函数都倒入当前的命名空间;你只是在当前命名空间中创建了一个名为my_module的变量,它指向那个模块对象。你需要通过 my_module.some_variable 来访问其内部的命名空间。

5.3 动态导入 (importlib)

💡 动态导入是指在程序运行时,根据字符串形式的模块名来导入模块。这与常规的、写死在代码里的import语句不同。Python的importlib标准库是实现动态导入的主要工具,特别是importlib.import_module()函数

python 复制代码
import importlib

# 假设这个模块名是从配置文件或用户输入中得到的
module_name_str = "math"

# 动态导入 'math' 模块
math_module = importlib.import_module(module_name_str)

# math_module 现在就等同于 "import math" 后的 math 对象
print(math_module.pi)       # 输出 3.141592653589793
print(math_module.sqrt(16)) # 输出 4.0

# 也可以动态导入包内的模块
pathlib_module = importlib.import_module("os.path") 
print(pathlib_module.basename("/a/b/c.txt")) # 输出 c.txt

5.4 模块的重载 (importlib.reload)

💡 模块重载(Reloading)是指重新执行一个已经被导入过的模块的顶层代码。Python默认只会导入一个模块一次,后续的import语句会直接从缓存(sys.modules)中获取。如果你在程序运行期间修改了模块的.py源文件,必须使用importlib.reload()来强制Python重新加载它,以使更改生效。这主要用于交互式开发和调试。

python 复制代码
import importlib
import my_config

# 假设 my_config.py 的内容是: SETTING = "original"
print(my_config.SETTING) # 输出: original

# --- 在这里,你手动去修改 my_config.py 文件 ---
# --- 将 SETTING 的值改为 "updated" 并保存 ---

# 再次打印,值不会变,因为模块没有被重新加载
print(my_config.SETTING) # 输出: original

# 使用 reload!
importlib.reload(my_config)

# 现在更改生效了
print(my_config.SETTING) # 输出: updated

了解即可,一般我们都会重新加载项目,也不会进行重启,这样还需要再代码中进行刷新,容易出错。

相关推荐
庙堂龙吟奈我何1 小时前
js中哪些数据在栈上,哪些数据在堆上?
开发语言·javascript·ecmascript
知忆_IS2 小时前
【问题解决】Label Studio上传文件数量超限解决方案
python·目标检测·label studio
武子康2 小时前
Java-169 Neo4j CQL 实战速查:字符串/聚合/关系与多跳查询
java·开发语言·数据库·python·sql·nosql·neo4j
一只小灿灿2 小时前
深入解析 Maven 与 Gradle:Java 项目构建工具的安装、使用
java·开发语言·maven
树在风中摇曳2 小时前
C语言 | 文件操作详解与实战示例
c语言·开发语言
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 小时前
MyBatis Plus中执行原生SQL语句方法
python·sql·mybatis
Q_Q5110082852 小时前
python+django/flask+vue的书城图书阅读器系统,亮点含目录章节pycharm
spring boot·python·django·flask·node.js·php
njsgcs2 小时前
excel提取长宽,进行排版导出ezdxf 装箱算法 贪婪 总利用率91%
开发语言·python·excel