2022-11-28 17:53:30 +08:00
|
|
|
|
---
|
|
|
|
|
title: 模块和包
|
|
|
|
|
description: Python 模块和包
|
|
|
|
|
keywords:
|
|
|
|
|
- Python
|
|
|
|
|
- 模块和包
|
|
|
|
|
tags:
|
|
|
|
|
- Python
|
|
|
|
|
sidebar_position: 7
|
|
|
|
|
author: 7Wate
|
2023-08-03 20:12:42 +08:00
|
|
|
|
date: 2023-08-03
|
2022-11-28 17:53:30 +08:00
|
|
|
|
---
|
2023-08-03 20:12:42 +08:00
|
|
|
|
在编程语言中,我们常常会看到各种不同级别的封装,如代码块、函数、类、模块,甚至包,每个级别都会进行逐级调用。**在Python中,一个`.py`文件就被看作是一个模块,这实际上是比类级别更高的封装。**在其他的编程语言中,被导入的模块通常被称为库。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
## 模块
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
在 Python 中,模块可以分为**自定义模块、内置模块(标准模块)和第三方模块。**使用模块主要有以下几个好处:
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
- **提高了代码的可维护性。**通过将代码拆分为多个模块,可以降低每个模块的复杂性,更容易进行理解和维护。
|
|
|
|
|
- **重用代码。**当一个模块编写完毕,就可以被其他的模块引用。这避免了“重复造轮子”,允许开发者更加专注于新的任务。
|
|
|
|
|
- **避免命名冲突。**同名的类、函数和变量可以分别存在于不同的模块中,防止名称冲突。但也要注意尽量避免与内置函数名(类名)冲突。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
### 自定义模块
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
自定义模块是开发者根据实际需求编写的代码,封装为模块方便在多个地方使用。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
### 标准模块
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
Python 拥有一个强大的标准库。Python语言的核心只包含数值、字符串、列表、字典、文件等常见类型和函数,而由 Python 标准库提供了系统管理、网络通信、文本处理、数据库接口、图形系统、XML 处理等额外的功能。如文本处理、文件系统操作、操作系统功能、网络通信、W3C 格式支持等。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
### 第三方模块
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
Python 拥有大量的第三方模块,这也是其核心优点之一。这些模块通常在 Python 包管理系统 [PyPI](https://pypi.python.org/) 中注册,你可以通过 pip 工具来安装。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
## 包
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
Python 为了避免模块名冲突,又引入了按目录来组织模块的方法,称为包(Package)。**包是模块的集合,比模块又高一级的封装。**包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。**一般来说,包名通常为全部小写,避免使用下划线。**
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-01-17 12:16:43 +08:00
|
|
|
|
### 标准包
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
标准包就是文件夹下必须存在 `__init__.py` 文件,该文件的内容可以为空。如果没有该文件,Python 无法识别出标准包。Python 中导入包后会初始化并执行 `__init__.py` 进行初始化;在 `__init__.py` 中,如果将`__all__` 定义为列表,其中包含对象名称的字符串,程序就可以通过 * 的方式导入。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```markdown
|
|
|
|
|
test.py
|
|
|
|
|
package_runoob
|
|
|
|
|
|-- __init__.py
|
|
|
|
|
|-- runoob1.py
|
|
|
|
|
|-- runoob2.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# package_runoob/runoob1.py
|
|
|
|
|
def runoob1():
|
|
|
|
|
print "I'm in runoob1"
|
|
|
|
|
|
|
|
|
|
# package_runoob/runoob2.py
|
|
|
|
|
def runoob2():
|
|
|
|
|
print "I'm in runoob2"
|
|
|
|
|
|
|
|
|
|
# package_runoob/__init__.py
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
print '作为主程序运行'
|
|
|
|
|
else:
|
|
|
|
|
print 'package_runoob 初始化'
|
|
|
|
|
|
|
|
|
|
# test.py
|
|
|
|
|
from package_runoob.runoob1 import runoob1
|
|
|
|
|
from package_runoob.runoob2 import runoob2
|
|
|
|
|
|
|
|
|
|
runoob1()
|
|
|
|
|
runoob2()
|
|
|
|
|
|
|
|
|
|
# 输出
|
|
|
|
|
# package_runoob 初始化
|
|
|
|
|
# I'm in runoob1
|
|
|
|
|
# I'm in runoob2
|
|
|
|
|
```
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
## 模块和包的导入
|
2022-12-03 18:18:23 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
Python 模块是一个包含 Python 定义和语句的文件,模块可以定义函数,类和变量。模块也可以包含可执行的代码。**包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。**
|
2022-12-03 18:18:23 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
### 导入方式
|
2022-12-03 18:18:23 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
在Python中,可以通过以下四种方式导入模块或包:
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
#### `import xx.xx`
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
这种方式将整个模块导入。如果模块中有函数、类或变量,我们需要以`module.xxx`的方式调用。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Module_a.py
|
|
|
|
|
|
|
|
|
|
def func():
|
2023-08-03 20:12:42 +08:00
|
|
|
|
print("This is module A!")
|
|
|
|
|
|
2022-11-28 17:53:30 +08:00
|
|
|
|
# Main.py
|
|
|
|
|
|
|
|
|
|
import module_a
|
2023-08-03 20:12:42 +08:00
|
|
|
|
module_a.func() # 调用函数
|
2022-11-28 17:53:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
#### `from xx.xx import xx`
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
这种方式从某个模块中导入某个指定的部分到当前命名空间,不会将整个模块导入。这种方式可以节省代码量,但需要注意避免名字冲突。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Main.py
|
|
|
|
|
|
|
|
|
|
from module_a import func
|
2023-08-03 20:12:42 +08:00
|
|
|
|
func() # 直接调用 func
|
2022-11-28 17:53:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
#### `from xx.xx import xx as rename`
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
为了避免命名冲突,我们可以在导入时重命名模块。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Main.py
|
|
|
|
|
|
|
|
|
|
from module_a import func as f
|
2023-08-03 20:12:42 +08:00
|
|
|
|
f() # 使用新名称 f 来调用函数
|
2022-11-28 17:53:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
#### `from xx.xx import \*`
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
这种方式将模块中的所有内容全部导入,非常容易发生命名冲突,因此需要谨慎使用。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
# Main.py
|
|
|
|
|
|
|
|
|
|
from module_a import *
|
|
|
|
|
|
|
|
|
|
def func():
|
2023-08-03 20:12:42 +08:00
|
|
|
|
print("This is the main module!")
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
func() # func 从 module_a 导入被 main 中的 func 覆盖
|
2022-11-28 17:53:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
2023-01-17 12:16:43 +08:00
|
|
|
|
### 模块路径搜索顺序
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
当我们尝试导入一个模块时,Python 解释器对模块位置的搜索顺序是:
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
1. Python 项目的当前目录
|
|
|
|
|
2. 在环境变量 PYTHONPATH 中列出的所有目录
|
|
|
|
|
3. Python 的安装目录和其他默认目录
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
模块搜索路径存储在`sys`模块的`sys.path`变量中。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
import sys
|
|
|
|
|
print(sys.path)
|
|
|
|
|
```
|
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
## 命名空间和作用域
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
在Python中,命名空间(Namespace)是从名称到对象的映射,主要用于避免命名冲突。命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
### 命名空间类型
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
![img](https://static.7wate.com/img/2022/11/20/7ee3813629181.png)
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
Python有三种命名空间:
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
- **内置名称(built-in names)**:Python语言内置的名称,如函数名`abs`、`char`和异常名称`BaseException`、`Exception`等。
|
|
|
|
|
- **全局名称(global names)**:模块中定义的名称,包括模块的函数、类、导入的模块、模块级别的变量和常量。
|
|
|
|
|
- **局部名称(local names)**:函数中定义的名称,包括函数的参数和局部定义的变量。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
### 作用域
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
作用域定义了命名空间可以直接访问的代码段,决定了在哪一部分程序可以访问特定的变量名。Python的作用域一共有4种,分别是:
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
![img](https://static.7wate.com/img/2022/11/20/1e3af056f1ac0.png)
|
|
|
|
|
|
|
|
|
|
- **L(Local)**:最内层,包含局部变量,比如一个函数/方法内部。
|
|
|
|
|
- **E(Enclosing)**:包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类)A里面又包含了一个函数B,那么对于B中的名称来说A中的作用域就为nonlocal。
|
|
|
|
|
- **G(Global)**:当前脚本的最外层,比如当前模块的全局变量。
|
|
|
|
|
- **B(Built-in)**: 包含了内建的变量/关键字等,最后被搜索。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
变量的查找顺序是:L --> E --> G --> B。
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
### 代码示例
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
|
|
|
|
```python
|
2023-08-03 20:12:42 +08:00
|
|
|
|
# 全局变量
|
|
|
|
|
x = 10
|
|
|
|
|
z = 40
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
# 定义函数 foo
|
|
|
|
|
def foo():
|
|
|
|
|
# 局部变量
|
|
|
|
|
x = 20
|
|
|
|
|
z = 50
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
def bar():
|
|
|
|
|
nonlocal z
|
|
|
|
|
print("局部变量 z =", z) # 输出 "局部变量 z = 50"
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
def baz():
|
|
|
|
|
global z
|
|
|
|
|
print("全局变量 z =", z) # 输出 "全局变量 z = 40"
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
print("局部变量 x =", x) # 输出 "局部变量 x = 20"
|
|
|
|
|
bar()
|
|
|
|
|
baz()
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
# 定义函数 outer
|
|
|
|
|
def outer():
|
|
|
|
|
y = 30 # 封闭作用域变量
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
def inner():
|
|
|
|
|
nonlocal y
|
|
|
|
|
print("封闭作用域 y =", y) # 输出 "封闭作用域 y = 30"
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
inner()
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
# 执行函数
|
|
|
|
|
foo()
|
|
|
|
|
print("全局变量 x =", x) # 输出 "全局变量 x = 10"
|
|
|
|
|
outer()
|
2022-11-28 17:53:30 +08:00
|
|
|
|
|
2023-08-03 20:12:42 +08:00
|
|
|
|
# 调用内置函数
|
|
|
|
|
print(abs(-5)) # 输出 5
|
2022-11-28 17:53:30 +08:00
|
|
|
|
```
|