1 Python 中的注释

1.1 注释的概念与作用

        注释(Comments)是嵌入在源代码中、为代码提供自然语言说明的语法元素。Python 解释器在程序运行之前会将其移除,因此注释不会对程序的执行结果产生任何影响。

        注释的主要作用体现在以下三个方面:

  • 阐明业务逻辑:将复杂的代码实现转化为直观的自然语言描述,辅助代码维护者快速且准确地理解程序的设计意图与业务背景。
  • 辅助代码调试:在排查异常时,可通过将特定代码段临时转换为注释以屏蔽其执行,进而逐步缩小并精准定位故障范围。
  • 促进团队协作:在多人参与的项目中,注释是开发者之间交流架构思想、标记待办事项以及警示潜在风险的标准化沟通媒介。

📚 扩展:特殊的指令型注释

        在 Python 工程中还存在一些写给操作系统或解释器解析的指令型注释,它们与解释程序逻辑的普通注释不同,属于底层配置。最常见的有两类:

  1. Shebang 指令:依据 PEP 394,该指令应位于脚本文件的第一行(例如 #!/usr/bin/env python3),用于在 Linux 或 macOS 系统中指定解释器路径,使脚本能被直接执行。
  2. 编码格式声明:依据 PEP 263,该声明应位于文件的前两行(例如 # -*- coding: utf-8 -*-),用于指定源文件的字符编码。这是 Python 2 时代防范中文报错的标配;由于 Python 3 已默认使用 UTF-8 编码,现代开发中已极少需要手动编写。

        你现阶段只需了解其存在,不必深入掌握。未来在接触跨平台部署或阅读早期开源项目源码时,会自然遇到这些知识。

1.2 单行注释的语法与排版规范

        在 Python 中,单行注释以井号(#)作为起始符号。从该符号直到当前行末尾的所有字符,均属于注释内容你只需在需要解释的语句上方或右侧书写井号及说明文字即可。

        为了理解单行注释在底层是如何被处理的,需要先了解 Python 程序的执行过程。当你运行一个 Python 程序时,源代码会依次经过词法分析语法分析编译解释执行等阶段。在词法分析阶段,字符序列被拆解为有意义的语法单元;随后进入语法分析阶段,依据 Python 的语法规则将这些单元组织为抽象语法树;接着编译器将抽象语法树转换为字节码;最后 Python 虚拟机逐条执行字节码指令

        单行注释在这一流程的词法分析阶段即被处理。当词法分析器遇到井号(#)时,会将该符号及其之后直到行末的所有字符全部忽略。这意味着单行注释在词法分析阶段即被完整剥离,根本不会进入后续的语法分析及编译执行阶段。

        为了保持代码的视觉整洁与团队风格的一致性,Python 官方第 8 号增强提案(Python Enhancement Proposal 8,简称 PEP 8对单行注释的排版提出了明确的建议:

  • 独立行注释:当注释单独占据一行时,井号应与代码语句保持相同的缩进层级。按照 PEP 8 的建议,你应在井号后紧跟一个半角空格,再书写注释文本
  • 行内注释:当注释与可执行代码位于同一行时,为避免视觉拥挤,PEP 8 建议你在代码语句与井号之间至少保留两个半角空格,并在井号后保留一个半角空格

        以下是一个遵循 PEP 8 排版规范的单行注释示例,其中包含了利用注释屏蔽特定代码的调试场景:

# 02_single_line_comment.py

# 打印系统启动的欢迎信息
print("系统正在启动...")

# 调试场景:暂时屏蔽下面这行输出,观察程序运行后的显示效果
# print("这是一行正在进行压力测试的冗余文本")

print("初始化环境成功!")  # 这一行用于标注当前程序已进入准备就绪状态
print("欢迎来到 Python 的世界。")  # 打印最终的问候语

        程序运行结果如下所示:

        可以看到,被注释掉的 print 语句没有执行,其余代码均正常输出。这直观地展示了单行注释屏蔽代码行以及为代码添加说明的实际效果。

💡 提示:PyCharm 提效技巧

        在 PyCharm 中,你可以使用快捷键 Ctrl + / 快速为当前行或选中的多行代码添加或取消单行注释。此外,还可以使用快捷键 Ctrl + Alt + L 触发代码格式化功能,该功能会自动依据 PEP 8 标准对缩进与空格进行校正,从工具层面确保代码风格的一致性。

1.3 多行注释的语法与使用方法

        当需要书写较长的说明文字时,单行注释需要每一行都重复书写井号,操作较为繁琐。为此,Python 提供了一种更为便捷的方式来实现多行注释。

        在 Python 中,多行注释由连续的三个单引号(''')或三个双引号(""")作为定界符包裹。你需要将注释内容书写在这对定界符之间,它们可以跨越多行。为了保持代码风格的高度统一,PEP 8 规范推荐你优先使用三个双引号。

        以下是一个多行注释的基本使用示例:

# 03_multiline_comment.py

print("程序主逻辑开始执行")

"""
这是一个多行注释块。
在此处可以自由地进行换行排版,用于解释下方代码的设计初衷。
Python 解释器会将其视为常规注释内容并跳过。
"""

print("程序主逻辑执行完毕")

        程序运行结果如下所示:

        从运行结果可以看到,三个双引号包裹的多行文字并未在终端中输出,它们仅作为注释存在,不影响程序的正常执行流程。

1.4 多行注释的底层本质与注意事项

本质上是未赋值的字符串

        严格从 Python 的语言设计来看,Python 并未为多行注释提供专属的语法符号。开发者在日常工程中习惯使用的多行注释,其物理形态实际上是一段独立的、没有被赋值给任何变量的多行字符串。当一段由三个引号包裹的文本没有作为数据保存到变量中,也没有参与任何运算时,它在程序中仅仅是一段孤立的文本。

        你需要留意单行注释与多行 “注释” 在底层处理机制上的关键差异。单行注释在词法分析阶段即被直接剥离,完全湮灭于解析流程的起始端。而作为注释使用的三引号字符串则不同:它会完整地通过词法分析和语法分析,并生成对应的抽象语法树节点。由于该字符串没有被赋值给任何变量,也没有参与任何运算,它对程序逻辑不产生实际影响,因此 Python 编译器在生成字节码之前会将其作为无用代码优化丢弃。正是利用这种 “生成节点但随后被优化移除” 的特性,你可以将未赋值的多行字符串作为大段的注释文本来使用。

多行注释严禁嵌套使用

        多行注释在 Python 中严禁嵌套使用。其原因在于多行注释的定界符匹配遵循就近匹配原则:解释器遇到一个起始定界符后,会立即将之后最先出现的定界符视为其结束定界符。当嵌套书写时,外层的起始定界符会与之后最先出现的定界符匹配,导致外层注释提前闭合,内层注释内容裸露为普通文本。裸露的文本不属于任何有效的 Python 语法结构,因此解释器会抛出语法错误。

        以下是一个嵌套错误的演示程序:

# 04_nested_comment_error.py

print("测试嵌套注释的错误场景...")

"""
这是外层多行注释的起始

    """
    这是内部尝试嵌套的注释,会导致外层字符串提前闭合
    """

由于外层定界符已经提前闭合,这段文字会被视为无效语法并引发错误!
"""

        运行上述代码,Python 解释器会抛出 IndentationError 错误,导致程序崩溃。程序运行结果如下所示:

显式输出时的物理属性

        基于多行注释本质上是真实字符串这一物理属性,当你使用 print 将其显式输出时,解释器便会将其作为有效数据处理,并按原本的排版格式(包括内部的换行和缩进)完整地打印到终端屏幕上。此时三个引号不再承担注释功能,而是定义了一段包含换行的字符串数据:

# 05_print_multiline_string.py

# 将多行字符串作为数据直接输出到终端
print("""
    第一行:显示当前系统版本
        第二行:显示当前运行环境
            第三行:显示欢迎访问信息
""")

        程序运行结果如下所示:

        与 1.3 节的示例对比可见,同样是用三个引号包裹的文字,在配合 print 使用时会被当作数据输出,而单独存在时则被当作注释忽略。这种差异源于多行注释本质上就是字符串这一物理属性。

📝 总结:注释的工程哲学

        优秀的代码本身应具备自解释性。在日常开发中,建议你优先通过清晰且具有描述性的命名来表达逻辑意图。只有当代码背后的业务规则过于复杂,或者需要解释特殊的设计决策与极端边界条件时,才应当辅以凝练且精准的注释。注释应随代码的变更及时维护,过时或错误的注释比没有注释更具误导性。


2 Python 中的变量

2.1 变量的概念

        在 Python 中,变量(variable)是程序中用于引用数据的一个名称。它本身并不存储数据,而是作为一个 “标签” 或 “名称牌”,附着在内存中的某个对象上。你可以通过这个名称来访问或操作它所指向的对象。

        你可以将变量想象成一个便利贴,上面写着名称;而对象则是内存中实际存放的数据。你可以随时将便利贴从一张桌子(对象)上揭下,贴到另一张桌子(另一个对象)上,但便利贴本身并不拥有桌子。

        变量没有固定的类型限制,同一个变量可以在程序运行的不同时刻指向不同类型的对象(例如先指向一个整数,再指向一个字符串)。这一特性使得 Python 在使用上非常灵活。

2.2 变量的创建与赋值

单个变量的创建

        在 Python 中,创建变量的语法非常简洁:

变量名 = 值

        其中等号(=)是赋值运算符,其左侧为变量名,右侧为任意表达式(可以是字面量、其他变量或运算结果)。Python 不需要提前声明变量类型,也不需要显式分配内存;赋值操作会自动完成变量的创建与绑定。

        每个变量在使用前必须先被赋值,否则将引发 NameError

        以下代码演示了将不同类型的字面量赋值给变量:

# 06_single_variable.py

# 整数类型
var1 = 2
# 浮点数类型
var2 = 3.14
# 字符串类型
name = "张三"

# print 函数支持同时输出多个项目,多个输出项之间用逗号分隔
print("整数变量:", var1)
print("浮点数变量:", var2)
print("字符串变量:", name)

        程序运行结果如下所示:

变量绑定的底层机制

        看到上述代码后,你可能会好奇:执行 name = "张三" 时,计算机内部究竟发生了什么?理解这一点,就能真正把握变量的本质。

        当你写入 name = "张三" 时,Python 解释器会执行以下操作:

  1. 在内存中创建一个字符串对象,其内容为 "张三"
  2. 将变量名 name 与该对象的内存地址进行绑定,即 name 指向该对象

        此后,你在程序中使用 name 时,Python 会通过该变量名找到其绑定的对象,并执行相应的操作。当 var1 = 2 时,同样会在内存中创建整数对象 2,并将标签 var1 贴上去。变量本身不存储数据,只存储指向数据的引用(内存地址)

多个变量的同时创建

        当需要定义多个变量时,Python 提供了两种高效的批量赋值方式,避免逐行重复书写。

链式赋值(多个变量指向同一个对象)

        当多个变量需要初始化为相同的值时,可以使用链式赋值:

var1 = var2 = var3 = 10

        上述代码中,整数对象 10 被同时绑定给 var1、var2 和 var3,三个变量名指向内存中的同一个整数对象

平行赋值(多个变量分别赋予不同的值)

        当需要分别赋给不同的值时,可以使用逗号分隔的平行赋值:

var4, var5, var6 = 10, 20, 30

        等号左侧的变量数量必须与右侧表达式的数量严格相等,否则会引发 ValueError

        以下完整示例展示了两种方式的用法:

# 07_multiple_variables.py

# 链式赋值:多个变量指向同一个值
x = y = z = 0
print("链式赋值后 x, y, z 的值:", x, y, z)

# 平行赋值:多个变量分别赋值
a, b, c = 5, 10, 15
print("平行赋值后 a, b, c 的值:", a, b, c)

# 平行赋值也支持右侧为变量或表达式
total = a + b
first, second = total, c - a
print("计算后 first, second 的值:", first, second)

        程序运行结果如下所示:

2.3 标识符的命名规范

        在 Python 中,所有由开发者自行命名的符号(变量名、函数名、类名、模块名等)统称为标识符(Identifiers)。为标识符命名时,需要同时遵守强制语法规则和 PEP 8 推荐的风格规范。

强制命名规则

  • 允许的字符:标识符只能包含英文字母(a-z,A-Z)、阿拉伯数字(0-9)和下划线(_)
  • 首字符限制:首字符不能是数字,必须是字母或下划线。例如 _var、var1 合法,1var 不合法。
  • 大小写敏感:Age 和 age 被认为是两个不同的标识符
  • 禁止使用关键字:Python 保留的具有特殊语法功能的关键字(如 if、for、class 等)不能用作自定义标识符

        Python 3 支持 Unicode 字符作为标识符(例如:年龄 = 25),但跨平台兼容性和团队协作中,强烈建议仅使用 ASCII 字母和下划线,避免因编码或字体问题造成混淆。

系统关键字

        Python 目前共有 35 个系统关键字,它们有固定的语法含义,不能被用作变量名。以下按功能分类列示,方便查阅:

功能分类 关键字列表 简要说明
布尔与空值 True,False,None 表示逻辑真、假以及空值对象
逻辑运算 and,or,not 逻辑与、或、非操作
成员与身份 in,is 包含关系测试、对象身份比较
条件分支 if,elif,else 条件判断控制流程
循环控制 for,while,break,continue 循环构建及跳转控制
结构定义 def,class,return,yield,lambda 定义函数、类、匿名函数及返回值
异常处理 try,except,finally,raise,assert 异常捕获、抛出与断言
模块引入 import,from,as 导入模块及设置别名
作用域声明 global,nonlocal 声明全局或非局部作用域变量
上下文管理 with 资源自动获取与释放
异步编程 async,await 声明异步函数及挂起等待
特殊操作 pass,del 空占位语句、删除对象引用

📚 扩展:获取当前版本关键字列表
        在 PyCharm 中创建脚本文件运行,或在 Python 交互式环境中执行以下代码,均可输出当前 Python 解释器版本下的所有关键字:

推荐命名风格

        在强制规则之上,为了提升代码可读性与团队一致性,PEP 8 给出了以下推荐风格:

  • 蛇形命名法(Snake Case):所有单词小写,单词间用下划线连接,例如 user_login_count。PEP 8 官方文档中明确推荐变量名和函数名使用此风格
  • 大驼峰命名法(Upper Camel Case):每个单词首字母大写,例如 DataBaseConfig。通常用于类名
  • 小驼峰命名法(Lower Camel Case):首单词全小写,后续单词首字母大写,例如 userLoginCount。在 Python 中不常用,但在其他语言(如 Java)中普遍。

        无论采用哪种风格,核心原则始终是:见名知意。应避免使用 a、b、tmp 等无业务语义的单字母来命名关键数据(循环计数器等极小作用域场景除外)。

2.4 变量的重新赋值

        变量名可以随时被重新赋值,即将其绑定到另一个对象上。这种操作会改变变量名所指的内存地址,原有的对象如果不再被任何变量引用,则会由 Python 的垃圾回收机制自动释放其占用的内存。

        以下代码演示了重新赋值的过程及引用变化:

# 08_variable_reassignment.py

# 初始绑定:current_status 指向字符串 "hello"
current_status = "hello"
print("修改前的值:", current_status)

# 重新绑定:current_status 现在指向新字符串 "world"
current_status = "world"
print("修改后的值:", current_status)

        程序运行结果如下所示:

底层变化示意:

  1. 执行第一行赋值时,内存中创建字符串对象 "hello",变量 current_status 存储该对象的地址。
  2. 执行第二行赋值时,内存中创建新字符串对象 "world",然后将 current_status 的指针更新为指向新对象的地址。
  3. 此时 "hello" 对象不再被任何变量引用,Python 的引用计数减为零,该对象会被垃圾回收器回收。

2.5 变量值的交换

        在编程中,经常需要交换两个变量所引用的数据。传统方法通常需要借助一个临时变量来暂存其中一个值,防止数据丢失。而 Python 提供了一种更简洁、更直观的写法,直接利用平行赋值完成交换。

        首先,展示传统的临时变量方法:

# 09_variable_swap.py

left_hand = "苹果"
right_hand = "香蕉"

print("交换前左手持有:", left_hand)
print("交换前右手持有:", right_hand)

# 传统方法:引入临时中转变量
temp = left_hand
left_hand = right_hand
right_hand = temp

print("----- 交换后 -----")
print("左手持有:", left_hand)
print("右手持有:", right_hand)

        程序运行结果如下所示:

        而 Python 推荐的优雅写法,仅用一行即可完成交换:

# 10_variable_swap_elegant.py

left_hand = "苹果"
right_hand = "香蕉"

print("交换前左手持有:", left_hand)
print("交换前右手持有:", right_hand)

# Python 的优雅写法:Python 特色平行赋值
left_hand, right_hand = right_hand, left_hand

print("----- 交换后 -----")
print("左手持有:", left_hand)
print("右手持有:", right_hand)

        运行该程序,终端显示与前一个示例完全相同的交换结果,最终左手持有 "香蕉",右手持有 "苹果"。

📚 扩展:一行交换的底层原理
        这种写法之所以安全,是因为 Python 在赋值过程中遵循 “先计算右侧,再赋值左侧” 的顺序。当执行 left_hand, right_hand = right_hand, left_hand 时,解释器会先评估右侧的表达式,将 right_hand 当前的值("香蕉")和 left_hand 当前的值("苹果")打包为一个临时的数据组合暂存在内存中。这个临时组合在 Python 内部实际上是一个元组(Tuple)。随后,再将该临时组合中的两个元素按顺序解包,分别赋值给左侧的 left_hand 和 right_hand。由于右侧的值在赋值开始前已被完整保存在该临时元组中,因此不会出现数据覆盖的问题。关于元组的详细语法与特性,将在后续的章节中进行系统性讲解。


3 Python 中的常量

3.1 常量的概念

        在程序运行过程中,其值始终保持不变的数据,称为常量(Constants)。常量通常用于存储数学常数(如圆周率 π)、系统配置参数(如最大连接数)或业务领域中固定不变的值(如税率、折扣率)。将这类固定值定义为常量,而非在代码中直接书写具体的数值(即 “硬编码”),可以显著提升代码的可读性与可维护性。

        从概念上区分,变量在程序执行期间可以被重新赋值,其指向的对象可以发生变化;而常量在语义上应保持不变。然而需要明确的是,Python 语言本身并未提供用于声明常量的专用语法或关键字,也没有在运行时强制实施 “值不可修改” 的保护机制。也就是说,Python 中并不存在其他编程语言(如 C 或 Java)中由 const 或 final 关键字所定义的那种严格意义上的常量。

        由于 Python 没有内置的常量类型,任何变量在技术上都可以被重新赋值。Python 社区通过一套约定俗成的命名规范来标识常量,以此向代码的阅读者传递 “此值不应被修改” 的意图。遵循这一约定主要依靠开发者的自律

3.2 常量的命名规范

        根据 Python 官方代码风格指南 PEP 8 的建议,常量的命名应遵循以下规范:

  • 全部大写:常量名称的所有字母均应使用大写形式
  • 下划线分隔:当常量名称由多个单词组成时,单词之间使用下划线(_)连接

        这种命名风格通常被称为 UPPER_SNAKE_CASE(全大写下划线命名法)

        以下是一些符合规范的常量命名示例:

# 11_constants_naming.py

MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
PI = 3.14159
SITE_NAME = "Python 学习站"

print("最大连接数:", MAX_CONNECTIONS)
print("默认超时时间:", DEFAULT_TIMEOUT)
print("圆周率:", PI)
print("站点名称:", SITE_NAME)

        程序运行结果如下所示:

3.3 常量与 “魔术数字” 的对比

        在编写代码时,直接在表达式中使用含义不明的数字或字符串,被称为 “魔术数字”(magic numbers)。这种做法会严重降低代码的可读性,因为阅读代码的人无法直观理解这些数值的业务含义。使用常量替代魔术数字是提升代码质量的典型实践。

不推荐的写法(硬编码魔术数字):

# 12_magic_numbers_bad.py

# 计算月度总收入:基础工资 5000 元,额外补贴 800 元
# 这里的 5000 和 800 含义不明确
total_income = 5000 + 800
print("月度总收入:", total_income)

        在上述代码中,5000 代表基础工资,800 代表额外补贴。如果没有注释说明,任何人都无法快速理解这些数字的实际意义。更为严重的是,如果这个补贴金额在多个地方被使用,当需要修改时,必须在每个出现的位置逐一查找和修改,极容易遗漏。

推荐的写法(使用常量):

# 13_constants_good.py

# 定义业务相关的常量
BASE_SALARY = 5000     # 基础工资
EXTRA_ALLOWANCE = 800  # 额外补贴

# 使用具有明确语义的常量名进行计算
total_income = BASE_SALARY + EXTRA_ALLOWANCE

print("基础工资:", BASE_SALARY)
print("额外补贴:", EXTRA_ALLOWANCE)
print("月度总收入:", total_income)

        程序运行结果如下所示:

        对比两段代码可以清晰地看到,使用常量的版本即使不借助注释,代码本身也具备极强的自解释性。当需要调整工资或补贴时,只需修改常量定义处的值,所有使用该常量的地方会自动生效。

3.4 常量的重新赋值问题

        前文已提及,Python 并未在语法层面禁止对常量进行重新赋值以下代码虽然在技术上是合法的,但在工程实践中必须严禁:

# 14_constants_reassignment_warning.py

PI = 3.14159
print("修改前的 PI:", PI)

# 技术上行得通,但绝对不要这样做
PI = 3.14
print("修改后的 PI:", PI)

        程序运行结果如下所示:

        尽管这段代码可以正常执行,且不会引发任何错误,但它严重违背了常量设计的初衷。当你将一个变量定义为全大写形式时,你便向所有阅读该代码的人(包括未来的自己)做出了明确的承诺:“这个值在整个程序的生命周期内不会改变。” 如果破坏了这一承诺,不仅会误导其他开发者,还可能在复杂的业务逻辑中引入难以追踪的逻辑错误。因此,必须严格遵守这一约定,绝不修改已定义为常量的变量的值。

        常量的使用核心在于约定与自律。它通过简洁的大写命名将固定数值赋予明确的业务含义,既消除了代码中晦涩的 “魔术数字”,又为后续的程序维护提供了统一的修改入口。虽然 Python 在语法层面未做强制约束,但作为专业开发者,严格遵守 PEP 8 的命名推荐并自觉维护常量的不变性,是写出清晰、健壮代码的基本素养。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐