在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。
为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。
Python 中的模块(Module)是一个包含 Python 定义和语句的文件,文件名就是模块名加上 .py 后缀。
模块可以包含函数、类、变量以及可执行的代码。通过模块,我们可以将代码组织成可重用的单元,便于管理和维护。
目录
1. 模块的作用
模块是包含 Python 定义和语句的文件,其作用包括:
-
将相关代码组织在一起,提高代码的可维护性
-
实现代码重用,避免重复编写相同功能
-
命名空间管理,避免命名冲突
-
便于大型项目的开发和维护
2. 导入模块
2.1 基本导入
-
导入整个模块
-
使用时需要通过模块名访问其中的内容:
module_name.function_name
import math
print(math.sqrt(16)) # 使用模块名.成员名访问
2.2 从模块导入特定内容
-
从模块中导入特定的函数、类或变量
-
使用时可以直接使用名称,无需模块名前
from math import sqrt, pi
print(sqrt(16)) # 直接使用函数名
print(pi) # 直接使用变量名
2.3 导入所有内容
- 导入模块中的所有内容(除了以下划线开头的)
- 可能导致命名冲突,不利于代码可读性
from math import * # 不推荐,可能导致命名冲突
print(sin(1.57))
2.4 给模块或导入项起别名
-
当模块名或函数名太长或有冲突时使用
import numpy as np
from math import factorial as fac
print(np.array([1, 2, 3]))
print(fac(5)) # 120
相对导入(在包内部使用)
-
主要用于包内部的模块相互引用
-
点号表示相对路径
from . import module_name # 同目录下的模块
from .. import module_name # 上级目录的模块
from .submodule import name # 子模块中的内容
3. __name__
属性
-
当模块作为主程序直接运行时:
-
__name__
的值被设置为'__main__'
-
-
当模块被导入到其他模块中时:
-
__name__
的值被设置为模块的名称(即文件名去掉.py
后缀)
-
3.1 模块测试代码
最常见的用法是在模块底部添加测试代码,这样当模块被直接运行时执行测试,而被导入时不执行:
def some_function():
print("这是一个函数")
if __name__ == '__main__':
# 只有直接运行这个模块时才会执行
print("模块作为主程序运行")
some_function()
3.2 防止导入时执行代码
# config.py
DEBUG = True
if __name__ == '__main__':
# 这部分代码不会在导入时执行
print("配置模块被直接运行")
3.3 提供命令行接口
# calculator.py
def add(a, b):
return a + b
if __name__ == '__main__':
import sys
if len(sys.argv) == 3:
a, b = map(int, sys.argv[1:3])
print(f"结果是: {add(a, b)}")
else:
print("用法: python calculator.py <数字1> <数字2>")
3.4 实际示例
文件结构:
project/
├── main.py
└── mymodule.py
mymodule.py:
def hello():
print("Hello from mymodule!")
print(f"在mymodule中,__name__ = {__name__}")
if __name__ == '__main__':
print("mymodule被直接运行")
hello()
main.py:
import mymodule
print("在main.py中")
mymodule.hello()
运行结果:
-
直接运行
mymodule.py
:在mymodule中,__name__ = __main__ mymodule被直接运行 Hello from mymodule!
-
运行
main.py
:在mymodule中,__name__ = mymodule 在main.py中 Hello from mymodule!
说明:每个模块都有一个 __name__ 属性。
__name__ 与 __main__ 底下是双下划线, _ _ 是这样去掉中间的那个空格。
4. dir()
函数
dir()
是 Python 的一个内置函数,用于查看对象的属性和方法列表,或者当前作用域内的名称空间。
4.1 基本用法
1. 不带参数调用 - 查看当前作用域的名称
x = 10
y = "hello"
def my_function():
pass
print(dir()) # 显示当前作用域中所有可用的名称
# 输出可能包含: ['__annotations__', '__builtins__', '__doc__', ..., 'my_function', 'x', 'y']
2. 带参数调用 - 查看对象的属性和方法
import math
# 查看math模块的所有属性和方法
print(dir(math))
# 输出可能包含: ['__doc__', '__loader__', '__name__', ..., 'cos', 'sin', 'sqrt']
my_list = [1, 2, 3]
# 查看列表的所有方法
print(dir(my_list))
# 输出可能包含: ['__add__', '__class__', ..., 'append', 'clear', 'copy', 'count', ...]
4.2 实际应用场景
1. 探索未知对象
import requests
# 查看requests模块的内容
print(dir(requests))
2. 调试时检查对象
class MyClass:
def __init__(self):
self.value = 42
def show(self):
print(self.value)
obj = MyClass()
print(dir(obj)) # 查看实例的属性和方法
3. 过滤出特定类型的成员
import math
# 获取math模块中所有不以双下划线开头的函数
math_funcs = [name for name in dir(math) if not name.startswith('__')]
print(math_funcs)
4.3 注意事项
-
dir()
返回的是一个字符串列表,包含对象的所有属性、方法和类成员的名称 -
结果可能包含"魔术方法"(以双下划线开头和结尾的方法)
-
对于自定义类,可以定义
__dir__()
方法来定制dir()
的输出 -
dir()
不会列出内置函数和内置类型的名称,除非传入特定对象
4.4 与 help()
结合使用
import math
# 先查看math模块有哪些内容
print(dir(math))
# 然后查看特定函数的帮助
help(math.sqrt)
4.5 自定义类的 __dir__
方法
class Person:
def __init__(self, name, age):
self.name = name
self._age = age # 约定为"私有"变量
def __dir__(self):
# 自定义dir()的输出,过滤掉以_开头的属性
return [attr for attr in super().__dir__() if not attr.startswith('_')]
p = Person("Alice", 30)
print(dir(p)) # 不会显示_age属性
5. 标准模块库
Python 自带丰富的标准库模块,例如:
-
os
- 操作系统接口 -
sys
- 系统相关参数和函数 -
math
- 数学函数 -
datetime
- 日期和时间处理 -
json
- JSON 编码和解码 -
re
- 正则表达式 -
random
- 生成随机数 -
urllib
- URL 处理 -
collections
- 容器数据类型
6. 包
包(Package)是Python组织模块的一种方式,它使用目录结构来组织相关的模块,使得项目结构更加清晰和可维护。
6.1 包的基本概念
包是一个包含__init__.py
文件的目录,该目录下可以包含多个模块或子包。
my_package/ # 包目录
├── __init__.py # 包的初始化文件
├── module1.py # 模块1
├── module2.py # 模块2
└── subpackage/ # 子包
├── __init__.py
└── module3.py
6.2 __init__.py
文件的作用
-
标识一个目录为Python包
-
可以包含包的初始化代码
-
可以定义
__all__
变量控制from package import *
的行为 -
可以简化导入语句
示例__init__.py
内容:
# 包级别的初始化代码
print(f"Initializing {__name__} package")
# 定义从包导入*时导入哪些模块
__all__ = ['module1', 'module2']
6.3 包的导入方式
基本导入
# 导入整个包
import my_package
my_package.module1.some_function()
# 导入包中的特定模块
from my_package import module1
module1.some_function()
# 导入子包中的模块
from my_package.subpackage import module3
相对导入(在包内部使用)
在包内部的模块中,可以使用相对导入:
# 在module2.py中导入同级的module1
from . import module1
# 在module3.py中导入上级包的module2
from .. import module2
# 在module3.py中导入上级包的module1中的特定函数
from ..module1 import some_function
6.4 包的实际应用示例
项目结构
calculator/
├── __init__.py
├── operations.py
├── advanced/
│ ├── __init__.py
│ └── scientific.py
└── utils.py
operations.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
scientific.py
from ..operations import add # 相对导入上级包的模块
def square(x):
return x * x
def cube(x):
return x * x * x
使用包
from calculator.operations import add, subtract
from calculator.advanced.scientific import square
print(add(5, 3)) # 8
print(square(4)) # 16
6.5 包的安装与分发
创建可安装的包
需要添加setup.py
文件:
from setuptools import setup, find_packages
setup(
name="calculator",
version="0.1",
packages=find_packages(),
)
安装本地包
pip install -e .
6.6 现代Python包结构(推荐)
对于更复杂的项目,推荐以下结构:
project_name/
├── src/
│ └── package_name/
│ ├── __init__.py
│ ├── module1.py
│ └── subpackage/
├── tests/
├── docs/
├── setup.py
├── pyproject.toml
└── README.md
6.7 命名空间包(Python 3.3+)
从Python 3.3开始,支持没有__init__.py
文件的命名空间包:
namespace_pkg/
├── module_a.py
└── module_b.py
6.8 最佳实践
-
保持
__init__.py
简洁,避免放入太多逻辑 -
使用明确的相对或绝对导入
-
避免循环导入
-
为包编写清晰的文档字符串
-
合理组织包结构,避免过深或过平的层次
包是Python模块化编程的核心概念,合理使用包可以大大提高代码的可维护性和复用性。