前言
Python 是一种简单易学、功能强大的编程语言,而其模块和包的概念更是为我们提供了一种有效管理和组织代码的方式。模块是 Python 程序的基本组成单元,它是一个包含函数、变量和类的文件。而包则是由多个模块组成的一个目录,可以帮助我们更好地组织和复用代码。
理解模块和包是编写大型、可维护 Python 项目的基础。本文将深入探讨模块的定义、导入机制、自定义方法以及包的创建与管理,帮助开发者构建清晰的代码结构。
Python 模块是包含函数、变量和类的文件,用于组织代码。包是由多个模块组成的目录,通过__init__.py 标识。导入模块的多种方式(import, from...import),自定义模块的方法,以及__name__== '__main__' 的作用。同时详细讲解了包的结构、__init__.py 的功能、相对导入与绝对导入的区别,以及 sys.path 搜索顺序。最后总结了最佳实践与常见错误,帮助开发者有效管理代码结构。

Python 是一种简单易学、功能强大的编程语言,而其模块和包的概念更是为我们提供了一种有效管理和组织代码的方式。模块是 Python 程序的基本组成单元,它是一个包含函数、变量和类的文件。而包则是由多个模块组成的一个目录,可以帮助我们更好地组织和复用代码。
理解模块和包是编写大型、可维护 Python 项目的基础。本文将深入探讨模块的定义、导入机制、自定义方法以及包的创建与管理,帮助开发者构建清晰的代码结构。
在 Python 中,模块是一种组织和复用代码的方式。它是一个包含函数、变量和类的文件,以 .py 为文件扩展名。模块可以被其他程序导入和使用,以提供特定功能或实现特定任务。
Python 模块使代码更易于管理和组织,同时也促进了代码的可重用性。通过将相关的功能放入不同的模块中,可以使代码更具可读性和可维护性。此外,模块还可以帮助开发人员对功能进行封装,以便在不同的项目中共享和复用代码。
在 Python 中,主要有以下几种方式来导入模块:
import 模块名from 模块名 import 功能名from 模块名 import *import 模块名 as 别名from 模块名 import 功能名 as 别名这是最标准的导入方式。当我们使用 import 模块名 的形式导入模块的时候,需要使用 模块名。功能 来调用模块中的方法。
import random # 导入 random 模块
result = random.randint(1, 10) # 生成 1-10 之间的随机数
print(result)
使用 import 模块名 也可以导入多个模块,例如 import module1, module2,但为了代码清晰,通常建议分开导入。
这种方式允许我们只导入该模块下的指定方法,并且调用方法的时候可以直接使用方法名,而不需要加模块名前缀。这简化了代码的书写,但在模块较多时可能导致命名冲突。
from random import randint # 导入 random 模块下的 randint 函数
result = randint(1, 10)
print(result)
from 模块名 import * 会导入该模块下的所有公共名称(即不以 _ 开头的名称)。这种方式虽然方便,但不推荐在生产环境中使用,因为它会污染当前命名空间,导致难以追踪变量来源。
from math import *
print(sqrt(9)) # 3.0
print(pow(3, 2)) # 9.0
注意:如果模块中定义了 __all__ 变量,则只会导入 __all__ 列表中指定的名称。
如果我们觉得模块名或者方法名太长写的时候太麻烦的时候,我们可以使用别名。这常用于避免命名冲突或简化长路径。
import time as t
t.sleep(2) # 程序会停止两秒再结束
from time import sleep as slp
slp(2)
当我们使用别名之后,之前的名字不能再继续使用,否则会报错。
在 Python 中,每个 Python 文件都可以作为一个模块,模块的名字就是文件的名字。也就是说自定义模块名必须要符合标识符命名规则(小写字母、数字、下划线,不能以数字开头)。
我们创建一个 python 文件,例如 my_module.py,并且在该文件中定义一个 add_num 方法。
# my_module.py
def add_num(a, b):
return a + b
我们可以对该模块内的 add_num 方法进行测试。如果在模块文件中直接运行测试代码,当该模块被其他文件导入时,这些测试代码也会执行,这通常不是我们想要的行为。
如何区分模块是被直接运行还是被导入?
使用 if __name__ == '__main__': 作为条件,来使该代码只会在该模块下执行,当该模块被其他文件导入时就不会执行测试代码。
__name__ 是系统变量,是模块的标识符。值是:如果是自身模块直接运行,值是 __main__,否则是模块所在的文件名。
我们在模块所在文件打印 __name__ 可以验证这一点。然后在导入该模块的文件下运行代码,可以看到 __name__ 的值变成了模块名。
修改后的模块代码如下:
# my_module.py
def add_num(a, b):
return a + b
if __name__ == '__main__':
print(add_num(1, 2)) # 仅在直接运行时输出 3
然后在导入该模块的文件下调用该模块的对应方法:
# main.py
import my_module
print(my_module.add_num(3, 5)) # 输出 8,不会触发上面的 print
这里并不会执行我们的测试代码,这样就解决了问题。
注意: 如果使用 from .. import .. 或 from .. import * 导入多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能。为了避免冲突,建议使用别名或明确指定模块前缀。
当导入一个模块,Python 解析器对模块位置的搜索顺序是:
PYTHONPATH 下的每个目录。可以通过 import sys; print(sys.path) 来查看具体的搜索路径列表。
注意事项:
random.py),否则会导致模块功能无法使用或覆盖标准库。from 模块名 import 功能 的时候,如果功能名字重复,调用到的是最后定义或导入的功能。当我们使用 from 模块名 import * 导入模块的时候,我们可以指定导入的方法,而不是全部方法。我们只需要在模块中定义 __all__ = ['方法 1', '方法 2'],这样就只会导入 __all__ 中的方法。
# my_module.py
__all__ = ['add_num']
def add_num(a, b):
return a + b
def sub(a, b):
return a - b
if __name__ == '__main__':
print(add_num(1, 2))
此时,在其他文件中执行 from my_module import *,只能访问到 add_num,访问 sub 会抛出 NameError。
Python 包是一种用于组织和管理 Python 代码的方式。它是一个包含模块、子包和资源文件的目录,可以在 Python 程序中被导入和使用。包的主要目的是将相关的功能和数据组织到一个单独的单元中,以便于重用和维护。
一个包通常包含一个名为 __init__.py 的特殊文件,用于标识该目录为一个包。它可以包含其他 Python 模块文件(以 .py 为扩展名)和子包(也是一个包的目录)。
在文件系统上,创建一个文件夹作为包名,并在其中放置 __init__.py 文件即可。在 IDE(如 PyCharm)中,右键点击目录选择 New -> Package 通常会自动创建该文件。
包结构示例:
mypackage/
__init__.py
module_a.py
module_b.py
subpackage/
__init__.py
module_c.py
我们首先需要在包下创建几个模块文件。
module_a.py
def add_num(a, b):
return a + b
module_b.py
def sub_num(a, b):
return a - b
导入包主要有两种方式:
当我们使用 import 包名。模块 导入包的时候,我们以 包名。模块名。功能 来使用相关功能。
import mypackage.module_a
import mypackage.module_b
print(mypackage.module_a.add_num(10, 20)) # 30
print(mypackage.module_b.sub_num(50, 20)) # 30
当我们使用 from 包名 import * 导入包的时候,我们需要在 __init__.py 文件中添加 __all__ = [] 来设置允许导入的模块列表。
mypackage/init.py
__all__ = ['module_a', 'module_b']
from . import module_a
from . import module_b
使用 模块名。功能 的方法来使用相应的功能。
from mypackage import *
print(module_a.add_num(10, 20)) # 30
print(module_b.sub_num(50, 20)) # 30
如果我们将 __init__.py 文件中的 __all__ 给删除或者注释掉的话,可能会导入包目录下所有的 .py 文件,这可能导致命名空间混乱。因此在使用 from 包名 import * 导入包的时候一定要记得设置 __init__.py 里的 __all__ 来设置允许导入的模块列表。
在包内部,可以使用相对导入来引用同一包下的其他模块。
# mypackage/module_a.py
from . import module_b # 相对导入
from .. import other_package # 向上两级导入
绝对导入则直接使用完整的包路径,例如 from mypackage import module_b。推荐使用绝对导入以提高代码的可移植性。
ImportError。应重构代码结构打破循环依赖。__init__.py:除了标记包之外,可以在其中初始化全局变量或暴露高层接口。Python 的模块和包系统是构建复杂应用的基础设施。通过合理划分模块、规范导入方式、利用 __init__.py 管理包内容,可以显著提升代码的可读性、可维护性和复用性。掌握 __name__、__all__ 以及搜索路径机制,能够帮助开发者解决常见的导入错误,编写出更加健壮的 Python 程序。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online