跳到主要内容 Python 文件结构与执行入口详解 | 极客日志
Python 算法
Python 文件结构与执行入口详解 本文详细解析了 Python 代码文件的八大组成部分,包括解释器声明、编码格式、文档字符串、模块导入、全局变量、类定义、函数定义及执行代码。重点阐述了 Python 无 main 函数入口的特性,解释了顶层代码自上而下执行的机制,并深入说明了 if __name__ == '__main__' 的作用及其在模块导入时的保护机制。文章还补充了编码一致性、导入时机及命名规范等最佳实践,帮助初学者构建规范的 Python 项目结构。
Python 文件结构与执行入口详解
1. Python 文件结构
通常,一个 Python 代码文件由以下 8 个主要部分组成:解释器版本声明(仅 Unix 环境)、文件编码格式声明、文档字符串、模块导入语句、全局变量定义、类定义、函数定义、执行代码。一个标准的模板代码如下:
os
DEBUG =
( ):
():
__name__ == :
my_func()
""" [3] 文档字符串 """
import
True
class
MyClass
object
""" [3] 文档字符串 """
pass
def
my_func
""" [3] 文档字符串 """
pass
if
'__main__'
1.1 解释器版本声明 (Shebang) 如果你学过 Linux 的 Shell 编程,这部分很容易理解。解释器声明又称为 Shebang 注释,它的标志是 #!。
Shebang 必须出现在脚本文件的第一行,后面用空白字符接解释器绝对路径,用于指明执行这个脚本的解释器,还可以带上参数。例如:
声明方式 说明 #!/usr/bin/python3指定具体路径 #!/usr/bin/env python3推荐方式,查找环境变量中的 python3
Shebang 的好处在于允许脚本和数据文件充当系统命令,无须在调用的时候由用户指定解释器,从而对用户或者其他程序隐藏了其执行的细节。但是 Shebang 只能在类 Unix 系统(如 Linux, MacOS 等)中起作用,Windows 系统会忽略此注释。
1.2 文件编码格式声明 这里的编码是指文件中字符的编码格式。目前世界上对于不同的语言和符号有许多不一样的编码格式,例如 ASCII 编码、ANSI 编码、UTF-8 编码、GBK 编码等。
编码的目的是把人类输入的文本字符转换为计算机认识的二进制字符,然后才能交给计算机处理;反过来,计算机处理后的二进制字符也需要转换为人类认识的文本字符,才能方便人类去阅读,这个过程称为解码。
如果我们不在脚本头部声明文件的编码格式,解释器会用操作系统默认的编码格式去处理脚本。如果默认的编码格式和你保存文件时使用的编码格式不一致,轻则文本显示乱码,重则数据读取异常,导致最终脚本运行出错。
因此,当我们创建一个新 Python 代码文件时,要习惯性地加上编码格式声明,而且这个声明必须要和你保存文件时使用的编码格式一致(一般文本编辑器或者 IDE 都支持选择文本编码格式)。我们一般约定使用 UTF-8 来作为代码文件的编解码格式。
注意: UTF-8 并不是 Python 的官方规定的编码格式,只是大家约定俗成且被广泛使用而已,而且大多数代码编辑器及 IDE 也是默认使用 UTF-8 作为文件编码格式的。
1.3 文档字符串 (Docstring) Python 的文档字符串又被称为 docstring,它既是注释,又是一个特殊的字符串常量(可以通过 __doc__ 方法来获取),用于提供关于 Python 模块、函数、类、方法或属性的描述或说明。它们通常被自动化工具(如 Pydoc 等)用来生成代码文档。
Python 文档字符串的主要用途是解释代码的功能和用途,以便其他开发者更容易理解和使用它。Python 官方规定了文档字符串的固定格式如下:
Python 文档字符串是以一对三个单引号或一对三个双引号括起来,放在模块、类、函数等开头的一行或多行注释型的字符串。
至于文档字符串的内容 Python 官方并没有提出硬性要求,可以是任意文本,但是建议要符合一定的格式要求,可以参考本账号后续发布的 Python 编程规范文档来设计你的文档字符串。
1.4 模块导入语句 前面课程说过,Python 编程的最大优点就是它拥有大量成熟的标准库及第三方扩展库。此外,我们在项目开发过程中,为了实现模块化编程,肯定也会设计各种自定义模块。在编写 Python 代码之前需要先导入这些库或者模块才能使用它们。一般会把导入库或导入模块的语句放到代码正文的最前面,在模块的文档字符串之后。
1.5 全局变量 Python 支持创建并使用全局变量。Python 代码中,只要你在一个函数之外声明一个变量,它就会自动成为一个全局变量。而且我们一般把全局变量的定义统一放在代码正文的前面,紧随导入模块语句之后。
1.6 类定义 Python 是一种面向对象编程的语言,所以 Python 中是可以定义和使用类的。我们一般会把类定义放在独立的 Python 模块文件中,通过导入模块文件的方式来使用。如果需要把类定义和使用放到同一文件下,建议把类定义放到代码的前部,全局变量定义之后,函数定义之前。
请注意,类只是一个模型,它不属于 Python 文件的执行代码,只能被其他程序去实例化引用,它自身在程序的运行过程中是无法被直接执行的。
1.7 函数定义 和其他大多数编程语言一样,Python 也是函数式编程,它主要依赖于数学函数的概念和逻辑,将程序看作是一系列函数的组合和调用。函数式编程的核心那肯定就是函数的定义了。
这里先不去教大家如何定义和使用函数,而是要说明一下,函数定义和类定义一样,它是一组功能代码的集合,只能被其他程序去调用,在程序的运行过程中它是不会自己运行的。我们一般把函数定义放在执行代码之前,任何函数都必须遵循先定义后调用的规则。
1.8 执行代码 执行代码也就是运行 Python 文件时会被直接执行的那部分代码,任何一个 Python 应用都必须含有执行代码。注意我说的是应用程序,如果你的 Python 代码只是打算封装成库或者被别人调用的模块,那就无需存在执行代码。
执行代码一般放在 Python 代码文件的最后面,使用 if __name__ == '__main__': 作为执行代码的起始语句。
这 8 个组成部分和排版顺序是创建和编写 Python 代码文件时比较完整的参考模板,但并不是说所有的 Python 代码文件都必须完整包含这 8 个部分。起码文档字符串不是 Python 程序运行的相关项,它只是给人看的;其他的部分也不是必选项,甚至完全可以创建一个空白的 Python 文件并执行它,它不会报错,只是没有意义而已。
你也可以不按上面要求的顺序来排版,例如你可以把导入库语句放到代码中的任意位置,而不必一定放在文件的前面,你只要保证在使用它之前完成导入就可以了;执行程序、类以及函数的定义顺序也是一样,你只需要保证在使用之前定义就 OK 了。只要你不嫌难看而且符合先定义或先导入后使用的约束规则,那你就完全可以把你的代码打散地放在文件的各个角落。
2. Python 执行入口 学过 C 语言的都知道,C 应用程序一定是从 main 函数开始执行的。但对于 Python 来说,它没有 main 函数入口一说,Python 代码的执行规则是:
Python 解释器按自上而下的顺序去执行 Python 文件中的顶层代码。
顶层代码 :在 Python 中,是以缩进来区分代码层次的,同一层次的代码缩进必须完全一致!所谓的顶层代码是指代码文件中缩进为 0 个空格的代码。注意,顶层代码包括所有有意义的 Python 语句,例如变量定义、类定义、函数定义、执行语句等都属于顶层代码范畴。
执行代码 :这里把顶层代码中具有逻辑执行意义的那部分代码称为执行代码,而全部变量定义、类定义、函数定义等定义性的代码称为定义代码。
print ("000" )
print ("111" )
print ("222" )
它的执行结果是自上而下执行,这是符合解释型语言的共同特征的。
结合上面对文件结构的讲解,我们一般把执行代码放到文件的最后面,且以 if __name__ == '__main__': 作为执行的入口,如下:
import os
class MyClass (object ):
pass
def my_func ():
print ("333" )
pass
if __name__ == '__main__' :
print ("000" )
print ("111" )
print ("222" )
思考:请问为什么'333'没有打印出来?因为我们前面说过了,类和函数只是一种模型或者方法的抽象,是不具备直接执行功能的,必须有可执行的代码去调用他们才会被执行。虽然 print("333") 本身是一条可执行的语句,但是它放到函数定义里面就属于函数功能的范畴了,而不是顶层的可执行代码,类里面的方法和执行语句也是一样的道理。
但注意了,Python 并没有规定一定是从 if __name__ == '__main__': 开始执行代码的,它的规则还是自上而下顺序执行。假如,在 if __name__ == '__main__': 之前如果添加一条可执行语句,那它肯定会先执行的。
import os
print ("333" )
class MyClass (object ):
pass
def my_func ():
pass
if __name__ == '__main__' :
print ("000" )
print ("111" )
print ("222" )
那它的执行结果就是先输出'333',因为自上而下的话 print("333") 是出现在最前面的!
你也许会问,那 if __name__ == '__main__': 还有什么意义?这可能就要先了解一下导入(import)的概念了。每一个 Python 文件都可以被另外的 Python 文件导入来使用,你可以理解为每个 Python 文件都是一个单一文件的库。
Python 规定:在 Python 代码中导入另外一个 Python 模块(或者脚本文件)时,Python 会执行该模块或脚本中的所有顶层代码。
print ("333" )
def my_func ():
print ("444" )
pass
if __name__ == '__main__' :
print ("000" )
print ("111" )
print ("222" )
执行 a.py,运行结果如下:可以看到只输出了'333'和'444',而'000、'111'、'222'这三条语句是没有被执行的。其中'333'属于 b.py 的顶层语句,按照上面提到的规则,它是在被导入时被执行的,'444'是正常被 a.py 的顶层语句 b.my_func() 执行出来的。
因此,执行代码入口 if __name__ == '__main__': 的声明是为了保护它的下属语句被其他程序导入时不被执行而使用的,当然,直接执行时又是可以被执行的。这一般用于编写库或者模块的单元测试代码。
3. 最佳实践与常见误区 为了编写更健壮的 Python 代码,除了掌握基本结构外,还需注意以下几点:
编码一致性 :始终使用 UTF-8 编码保存文件,并在文件头声明 # -*- coding:utf-8 -*-,避免跨平台运行时出现中文乱码。
Shebang 的使用 :在 Linux/MacOS 环境下,确保脚本具有执行权限 (chmod +x script.py) 并正确设置 Shebang,以便可以直接运行脚本。
导入时机 :尽量将 import 语句放在文件顶部,避免在循环或函数内部重复导入,这不仅影响性能,也降低代码可读性。
命名规范 :遵循 PEP 8 规范,模块名使用小写加下划线,类名使用大驼峰,函数名使用小写加下划线。
入口保护 :在开发库或模块时,务必使用 if __name__ == '__main__': 包裹测试代码,防止被其他模块导入时意外触发。
通过以上结构和规范的建立,可以显著提升 Python 项目的可维护性和专业性。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online