知行LEARNING HANDBOOK
目录 · N01 Python 内置机制、模块与文档字符串
学习手册/计算机基础/编程语言基础
N016 分钟更新于 2026-08-20

Python 内置机制、模块与文档字符串

系统整理 Python 内置命名空间、assert、文档字符串、模块与 __name__。

pythonbuiltinsassertdocstringmodule__name__

建议先了解

  • Python 基础语法

1. 这篇笔记解决什么问题

本篇整理 Python 中几个容易混淆但非常基础的机制:builtins、内置常量、assert、注释与 docstring、源文件编码声明、模块与 __name__

学完后应能回答:为什么 print 不需要导入;Python 有没有真正的常量;assert 应该检查什么;为什么三引号不是多行注释;一个 .py 文件与 module 是什么关系;__name__ == "__main__" 到底在判断什么。

2. 前置知识

  • Python 变量、函数、类的基础语法。
  • import 的基本用法。

待补充:LEGB 名字解析、import system 的完整实现。

3. 核心概念

3.1 内置命名空间(Built-in Namespace)

以下名字通常可以直接使用:

print("hello")
len([1, 2, 3])
int("123")
list()
dict()

它们来自 builtins 模块:

import builtins
builtins.print("hello")

名字查找常用 LEGB 模型理解:

Local → Enclosing → Global → Builtins

3.2 Python 的“常量”

Python 没有用户可声明的通用运行时 const 机制。

PI = 3.1415926
PI = 100   # 仍然合法

全大写只是约定。typing.Final 可辅助静态检查:

from typing import Final
PI: Final = 3.1415926

但它不是运行时强制不可重新绑定。

常见内置常量包括:

True
False
None
NotImplemented
Ellipsis
__debug__

3.3 assert

assert 表达的是:

按照程序设计,这个条件在这里必须成立;若不成立,通常说明内部逻辑有 bug。

例如:

assert hidden_size % num_heads == 0
assert 0 <= probability <= 1

它适合检查内部不变量,不适合承担权限、密码、业务规则、外部输入校验。

普通运行时 __debug__ 通常为 True;使用 python -O 时会进入优化模式,assert 不应被依赖为程序正常逻辑的一部分。

3.4 注释与文档字符串(Docstring)

Python 真正的注释语法只有:

# comment

Python 没有 C 风格 /* ... */ 多行注释。

三引号:

"""
hello
world
"""

本质是字符串字面量,不是注释。

若字符串字面量出现在模块、类、函数或方法主体的第一条语句位置,则会成为 docstring:

def add(a, b):
    """Return the sum of a and b."""
    return a + b

可读取:

print(add.__doc__)
help(add)

3.5 文件编码声明

# coding: utf-8

或:

# -*- coding: utf-8 -*-

描述的是 .py 源文件字节如何解码为源码字符,必须位于文件前两行。

它与运行时:

"你好".encode("utf-8")

不是同一阶段。前者是“读源码”,后者是“运行时 strbytes”。

3.6 模块(Module)

普通 .py 文件通常是一个模块的源码载体:

project/
├── main.py
└── math_utils.py
import math_utils
print(type(math_utils))
# <class 'module'>

严格来说 module 不一定来自 .py:也可能是内置模块或 C 扩展模块。

3.7 __name__

__name__ 可以理解为模块的一个重要属性。

直接执行:

python a.py

此时:

__name__ == "__main__"

被导入:

import a

a.py 中的 __name__ 通常为 "a";包内模块可能为 "pkg.mod"

经典入口:

def main():
    ...

if __name__ == "__main__":
    main()

含义是:只在该模块作为入口直接执行时运行 main()

4. 直观理解

builtins 可以理解为名字解析的最后一层公共名字空间;assert 是开发者给内部逻辑安装的“早期报警器”;docstring 是“被 Python 保存的说明字符串”;__name__ 则记录模块当前以什么身份被加载。

这些机制看起来都像“特殊语法”,但实际上处于不同层次:名字解析、运行时自检、源码解析、模块元数据。

5. 工作原理

5.1 三引号字符串的三种情况

"""abc"""
    │
    ├── 赋值给变量 → 普通字符串
    ├── 位于 module/class/function 开头 → docstring
    └── 单独放在其他位置 → 无用字符串表达式

5.2 __name__ 的两个典型状态

文件 a.py
   │
   ├── 直接运行 → 模块名 __main__
   └── 被 import → 模块名 a(或完整限定名)

6. 示例

示例 1:assert 适合内部不变量

hidden_size = 768
num_heads = 12
assert hidden_size % num_heads == 0

这里条件失败通常意味着模型配置或程序逻辑不满足自身设计。

示例 2:用户输入不要依赖 assert

age = int(input("age: "))
if not 0 <= age <= 150:
    raise ValueError("invalid age")

外部输入非法是一种正常可能出现的运行情况。

7. 代码、命令或公式

import builtins
print(dir(builtins))
python script.py
python -O script.py
python -OO script.py

[!WARNING] -O / -OO 的精确优化行为和版本细节应以对应 Python 官方文档再次核验,不应把本次对话中的描述当成跨版本实现保证。

8. 容易混淆的概念

概念 A概念 B核心区别
大写变量真正 const大写只是约定
typing.Final运行时不可修改Final 主要服务静态检查
#"""..."""前者是真注释,后者是字符串
docstring普通字符串docstring 是出现在特殊首语句位置的字符串
源文件编码str.encode()前者处理源码 bytes,后者处理运行时字符串
.py 文件module 对象前者是源码载体,后者是运行时对象
__main__main()前者是入口模块名,后者只是普通函数命名习惯

9. 常见误区

误区:Python 有真正的三引号多行注释

错误原因:单独字符串表达式通常没有可见效果。

正确理解:三引号始终产生字符串字面量;只有 # 是注释语法。

如何验证:把它放在函数第一行,再查看 func.__doc__

误区:assert 是通用输入范围校验

错误原因:混淆内部不变量与外部错误。

正确理解:内部“不应该失败”的假设适合 assert;业务和安全校验使用显式条件与异常。

如何验证:比较普通运行和优化运行。

误区:一个 .py 文件在任何意义下都严格等于 module

错误原因:忽略内置模块与扩展模块。

正确理解:普通 .py 文件通常对应模块源码,但 module 是更广的运行时概念。

10. 与其他知识的关系

11. 可以亲手完成的验证

实验目标:验证 __name__、docstring 和 assert

  1. 创建 a.py,打印 __name__
  2. 直接运行,再从 b.py 导入;
  3. 给函数加入 docstring,打印 .__doc__
  4. 加入一个失败的 assert
  5. 分别用普通模式与 -O 运行。

预期:__name__ 随加载身份变化;docstring 可读取;assert 不应作为业务逻辑依赖。

实验不能证明:不同 Python 实现的底层加载和优化实现完全相同。

12. 尚未解决的问题

  • import system 的查找、加载、缓存流程未展开。
  • sys.modules 的角色未展开。
  • builtins namespace 在 CPython frame 中如何连接尚未展开。
  • docstring 在 -OO 下的精确版本行为需要官方文档核验。

13. 自测问题

  1. 为什么 print() 不需要 import builtins
  2. PI = 3.14 为什么不是真正语言层面的常量?
  3. 什么条件适合使用 assert
  4. 为什么 """...""" 不是多行注释?
  5. # coding: utf-8.encode("utf-8") 有什么区别?
  6. 直接执行模块和导入模块时,__name__ 分别是什么?
参考答案
  1. 因为 builtins 名字位于 built-in namespace,名字解析可直接找到。
  2. Python 没有用户可声明的通用运行时 const 机制,大写只是约定。
  3. 程序内部设计上必须成立、失败通常意味着 bug 的条件。
  4. 因为它始终是字符串字面量,只是在某些位置会成为 docstring。
  5. 前者决定源码文件 bytes 如何解码,后者把运行时 str 编码为 bytes
  6. 直接执行为 __main__;被导入时通常为模块名或限定模块名。

14. 一句话总结

Python 的 builtins、assert、docstring、编码声明和 __name__ 分别属于名字解析、自检、文档元数据、源码解码和模块身份机制;它们应按语义层次区分,而不是都当成“特殊语法”。