Python 编程:脚本、模块、包与应用开发全解析
1. 类与对象基础
在 Python 中,定义类和使用类的对象是编程的基础操作。类的行为由其方法来定义,而类的内部状态则是各种方法作用的结果。与其他一些语言不同,Python 并不需要正式声明实例变量,通常依靠
__init__()
方法为对象的状态提供初始或默认值。
Python 在解析属性和方法名时,会按照对象、类,然后是超类的顺序进行搜索。方法解析顺序基于类在初始类语句中的呈现顺序。
这里有两个重要的装饰器:
-
@properties
装饰器:可用于创建具有与属性相同语法的方法,有助于简化复杂的算法。
-
@staticmethod
装饰器:用于创建属于整个类且独立于类的任何特定实例的方法。
为了节省内存,可以使用
__slots__
变量。它会构建一个不基于字典来存储属性的对象,这样的对象占用内存较小,但也存在一些局限性。另外,还可以创建可调用对象,这种对象可以像函数一样使用,同时具备对象的强大功能。
2. 脚本、模块、包和应用的概念
虽然在 Python 的交互式环境(REPL)中进行编程很方便,但我们的最终目标是创建 Python 应用文件。Python 文件可以分为以下几种类型:
-
脚本
:当由 Python 程序执行时,能够完成一些有用的工作。
-
模块
:设计用于被导入,以提供有用的定义。
-
包
:包含 Python 模块的目录。
除了这些正式定义的概念,还有一些更通用的术语,如库、应用程序或框架,虽然语言没有对它们进行正式定义,但在 Python 中有实现这些概念的方法。例如,一组模块或包可以被视为一个库,Python 标准库就是一个大型的模块和包集合。一个“应用程序”至少包含一个脚本,更复杂的应用程序可能涉及一个脚本以及多个额外的模块和包。框架则是一个 Python 应用程序,我们可以将自定义的模块或包注入其中,许多框架还会包含非 Python 文件,如 Web 框架可能包含大量的 HTML 和 CSS,GUI 框架可能包含图像文件和字体。
3. 脚本文件规则
Python 脚本文件必须遵循一个简单的规则:必须是纯文本文件。此外,还有两条建议通常很有帮助:
-
内容
:必须是纯文本,理想情况下使用 UTF - 8 编码,不过 ASCII 编码也很常见。
-
文件名
:应遵循 Python 标识符规则,以字母开头,只使用字母、数字和下划线
_
。以双下划线
__
开头和结尾的文件名是保留的,对 Python 有特殊含义。
-
文件扩展名
:应该是
.py
。
这两条额外的建议对于编写模块和包是必不可少的,但对于编写简单脚本并非必需。脚本只是一系列语句,与在 REPL 提示符下执行的操作类似,唯一的区别是脚本没有隐式的打印输出,必须使用
print()
函数才能看到结果。在大型应用程序中,通常使用
logging
模块来生成更复杂的输出。随着应用程序的成熟,有时会将早期使用的
print()
函数替换为
logging.debug()
函数。
4. 运行脚本的方法
运行脚本需要将其作为输入提供给 Python 程序,常见的方法有以下三种:
-
按文件名运行
:这是最常见的方法,将文件名提供给 Python 命令即可。例如,假设在
Chapter_12
目录下有一个名为
ch12_script1.py
的文件,在 Linux 和 Mac OS X 中,完整的文件名是
Chapter_12/ch12_script1.py
,在 Windows 中是
Chapter_12\ch12_script1.py
。以下是在 Linux 系统上按文件名运行脚本的示例:
MacBookPro - SLott:Code slott$ python3 Chapter_12/ch12_script1.py
Temperature °C: 8
C = 8°, F = 46°
脚本文件
ch12_script1.py
的内容如下:
c = float(input("Temperature °C: "))
f = 32 + 9 * c / 5
print("C={c:.0f}°, F={f:.0f}°".format(c=c, f=f))
-
按模块名运行
:在大多数情况下,可以将脚本安装到 Python 库的
site-packages目录中,或者使用PYTHONPATH环境变量扩展 Python 路径,使脚本文件在 Python 的搜索路径中可见。-
安装到
site-packages目录 :可以使用 Python 的distutils包,创建一个setup.py文件来描述要安装的模块,然后运行python3 setup.py install将模块安装到site-packages目录。像pip和easy - install这样的安装程序也遵循这种标准模式。也可以手动找到site-packages目录并将模块复制到该目录,该目录在不同的操作系统中位置不同,它是sys.path变量的最后一项。 -
设置
PYTHONPATH环境变量 :在 Linux 中,可以使用export命令更改环境变量,通常将其放在~/.bash_profile文件中。在 Windows 中,需要在高级系统设置中更改环境变量。通过设置PYTHONPATH变量,可以轻松创建包含多个模块的私有库。
-
安装到
一旦模块在 Python 的搜索路径中可见,就可以使用
-m
选项按模块名运行脚本。例如:
MacBookPro - SLott:Code slott$ python3 -m Chapter_12.ch12_script1
Temperature °C: 8
C = 8°, F = 46°
-
使用操作系统 shell 规则运行
:在 Linux 和 Mac OS X 中,需要使脚本文件可执行,并在文件的第一行设置与 Python3 程序的关联。通常使用
#!/usr/bin/env python3作为文件的第一行,这将使用操作系统的env程序来定位并启动 Python3 环境。使用chmod +x命令将文件标记为可执行,例如:
MacBookPro - SLott:Code slott$ chmod +x Chapter_12/ch12_script1.py
在 Windows 中,所有文件都被视为可执行的,文件扩展名与程序的关联通过 Windows 控制面板设置,在安装 Python 时就已经完成了设置。一旦文件被标记为可执行,就可以直接提供文件名来运行脚本。
5. 选择好的脚本名称
脚本名称应该简短且有意义,与文件名一样,最好避免使用复杂的前缀和后缀。Linux 或 Windows DOS 命令为脚本命名提供了一些参考,例如
git
命令,它使用简单的命令名作为前缀,通过不同的子命令来完成各种操作。
argparse
模块可以很好地支持这种方式,我们可以定义适用于所有子命令的通用参数,也可以定义每个子命令独有的参数。
6. 创建可重用模块
在 Python 中,模块是软件复用的基本单位。当某个功能需要在多个脚本中出现时,可以将该功能放在一个模块中,并在需要使用该功能的每个脚本中导入该模块。“复用”有两种不同的含义:
-
局部复用
:可以定义类层次结构,通过继承在应用程序内部实现局部复用,通常将相关的类定义在一个模块文件中。
-
跨应用复用
:定义一个模块,实现跨应用程序的复用。
要创建一个可导入的模块,只需确保 Python 文件位于 Python 搜索路径中的某个目录中。由于当前工作目录始终是可见的,因此可以通过在当前工作目录中创建文件来创建模块。
设计用于导入的模块主要由
import
、
class
和
def
语句组成,也可以使用赋值语句创建模块全局变量,但需要注意处理的工作量。通过赋值、类定义、函数定义或导入创建的任何名称都将位于该模块的命名空间中。
一个模块只会被导入一次,导入实现会检查已加载模块的全局缓存(
sys.modules
),以确定模块是否已被导入。因此,一个实际进行某些处理的模块只会执行一次处理,之后的导入将被忽略,这使得在导入的模块中创建全局单例对象变得容易。不过,一般情况下,我们期望
import
语句提供类、函数和模块全局变量的定义,而不是进行有用的处理。
模块还可以定义独特的异常,例如在模块中创建一个名为
Error
的通用异常类:
class Error(Exception): pass
当导入该模块时,可以通过
some_module.Error
来引用这个异常:
import some_module
try:
some_module.some_function()
except some_module.Error as e:
logger.exception("some_function broke: {0}".format(e))
7. 创建混合库/应用模块
脚本通常会导入模块,可能会定义一些函数或类,并进行相关的处理。但这种简洁的脚本编写方式存在一些缺点,例如难以进行单元测试,因为每个单元测试都需要将脚本作为子进程调用,这可能会涉及大量的操作系统开销。而且,随着应用程序的成熟,一个好的脚本可能会成为更大、更全面的应用程序的一个组件,从脚本文件创建复合应用程序会变得困难,而从函数或类创建复合进程则要容易得多。
因此,建议采用以下脚本结构:
def c_to_f():
c = float(input("Temperature °C: "))
f = 32 + 9 * c / 5
print("C={c:.0f}°, F={f:.0f}°".format(c=c, f=f))
if __name__ == "__main__":
c_to_f()
通过将脚本封装在一个函数中,并使用
if __name__ == "__main__"
语句来区分主脚本和导入的模块。当模块被导入时,Python 将全局变量
__name__
设置为实际的模块名称;当作为主脚本运行时,Python 将
__name__
设置为
__main__
。
这种模式还可以用于编写运行自身单元测试的库模块,例如在一个从不作为主脚本使用的库模块中,可以包含以下代码:
if __name__ == "__main__":
import doctest
doctest.testmod(verbose=1)
这将运行嵌入在文档字符串中的所有单元测试。
8. 创建包
包是一个包含模块文件和一个额外文件的目录,每个包必须有一个
__init__.py
文件,该文件通常为空。
《Python 之禅》建议“扁平优于嵌套”,即尽可能将 Python 应用程序组织成扁平的模块集合,深度嵌套、复杂的包层次结构并不被认为是有益的。
可以通过两种方式使用包:
-
导入包中的模块
:例如,Python 标准库中的
XML
包包含多个 XML 解析器模块,可以使用
import xml.etree
导入
XML
包中的
etree
模块。
-
将包作为一个整体导入
:例如,当编写
import collections
时,实际上是在导入
collections/__init__.py
模块。
以下是一个简单的流程图,展示了 Python 脚本、模块和包的关系:
graph LR
A[Python 应用] --> B[脚本]
A --> C[模块]
A --> D[包]
C --> E[可重用功能]
D --> F[模块文件]
D --> G[__init__.py]
通过以上对 Python 脚本、模块、包和应用的详细介绍,我们可以更好地组织和开发 Python 程序,提高代码的复用性和可维护性。在实际编程中,根据具体需求选择合适的方式来创建和使用脚本、模块和包,能够让我们的开发工作更加高效。
Python 编程:脚本、模块、包与应用开发全解析
9. 不同类型文件的对比
为了更清晰地理解脚本、模块和包的区别,下面通过表格进行对比:
| 文件类型 | 定义 | 特点 | 示例 |
| ---- | ---- | ---- | ---- |
| 脚本 | 能在被 Python 程序执行时完成有用工作的文件 | 是一系列语句,无隐式打印输出,需用
print()
函数显示结果 |
ch12_script1.py
|
| 模块 | 设计用于被导入以提供有用定义的文件 | 主要由
import
、
class
和
def
语句组成,可实现软件复用 | 包含通用功能函数的模块文件 |
| 包 | 包含模块文件和
__init__.py
文件的目录 | 可组织多个相关模块,有扁平组织的建议 |
XML
包 |
10. 运行脚本方法的选择
在实际开发中,选择合适的运行脚本方法很重要,以下是不同方法的适用场景:
-
按文件名运行
:适用于快速测试脚本,无需考虑模块导入和环境配置,直接执行脚本文件。
-
按模块名运行
:当脚本需要作为模块被其他程序导入,或者需要在不同环境中使用时,可将其安装到
site-packages
目录或设置
PYTHONPATH
环境变量,然后按模块名运行。
-
使用操作系统 shell 规则运行
:在需要将脚本作为可执行文件,像系统命令一样直接运行时使用,方便快捷。
下面是一个 mermaid 流程图,展示了选择运行脚本方法的决策过程:
graph TD
A[开始] --> B{是否需要快速测试?}
B -- 是 --> C[按文件名运行]
B -- 否 --> D{是否需要作为模块导入?}
D -- 是 --> E[按模块名运行]
D -- 否 --> F{是否需要作为可执行文件运行?}
F -- 是 --> G[使用操作系统 shell 规则运行]
F -- 否 --> H[重新评估需求]
11. 模块复用的优势与实践
模块复用带来了诸多优势,如提高代码的可维护性和开发效率。以下是一些实践建议:
-
功能拆分
:将不同的功能拆分成独立的模块,每个模块负责单一的功能,便于管理和复用。
-
接口设计
:为模块设计清晰的接口,明确输入和输出,方便其他脚本或模块调用。
-
版本管理
:对模块进行版本管理,确保在不同项目中使用的模块版本一致。
12. 异常处理在模块中的应用
在模块中定义异常可以增强代码的健壮性。除了前面提到的创建通用异常类
Error
,还可以根据具体业务需求创建更细致的异常类。例如:
class InputError(Exception):
"""当输入不符合要求时抛出的异常"""
pass
def divide_numbers(a, b):
if b == 0:
raise InputError("除数不能为零")
return a / b
在调用该函数时,可以捕获并处理自定义异常:
try:
result = divide_numbers(10, 0)
except InputError as e:
print(f"发生错误: {e}")
13. 混合库/应用模块的扩展应用
混合库/应用模块的设计模式不仅可以用于单元测试,还可以用于其他场景。例如,在开发一个命令行工具时,可以根据不同的命令行参数执行不同的功能:
import argparse
def command_one():
print("执行命令一")
def command_two():
print("执行命令二")
def main():
parser = argparse.ArgumentParser(description="命令行工具示例")
subparsers = parser.add_subparsers(title="子命令", dest="command")
parser_one = subparsers.add_parser("one", help="执行命令一")
parser_two = subparsers.add_parser("two", help="执行命令二")
args = parser.parse_args()
if args.command == "one":
command_one()
elif args.command == "two":
command_two()
if __name__ == "__main__":
main()
在这个示例中,通过
argparse
模块解析命令行参数,根据不同的子命令调用不同的函数。
14. 包的组织与管理
在组织包时,要遵循扁平优于嵌套的原则。以下是一些包组织的建议:
-
功能分组
:将相关的模块放在同一个包中,便于管理和查找。
-
避免过度嵌套
:尽量减少包的嵌套层次,保持结构清晰。
-
文档编写
:为包和模块编写详细的文档,方便其他开发者使用。
15. 总结
通过对 Python 脚本、模块、包和应用的深入学习,我们了解了它们的概念、创建方法和使用场景。在实际开发中,合理运用脚本、模块和包可以提高代码的复用性、可维护性和开发效率。同时,掌握不同的运行脚本方法和异常处理技巧,能够让我们更好地应对各种开发需求。希望这些知识能够帮助你在 Python 编程的道路上更加得心应手。
以下是一个简单的列表,总结了本文的关键要点:
1. 掌握类与对象的基础,包括方法定义和属性解析。
2. 理解脚本、模块、包和应用的概念及区别。
3. 遵循脚本文件的规则,选择合适的文件名和扩展名。
4. 学会三种运行脚本的方法,并根据需求选择合适的方式。
5. 选择简短且有意义的脚本名称,使用
argparse
模块管理命令行参数。
6. 创建可重用模块,实现局部和跨应用的复用。
7. 运用混合库/应用模块的设计模式,方便单元测试和复合应用开发。
8. 合理组织包,遵循扁平优于嵌套的原则。
通过不断实践和探索,你将能够更加熟练地运用这些知识,开发出高质量的 Python 程序。
超级会员免费看

被折叠的 条评论
为什么被折叠?



