前言
把「安装」和「变量名」放在一篇里讲,是因为它们对应初学者的两个阶段:装好解释器之后,你写下的第一行代码大概率是x = 1或者name = "张三",而这一行里就包含了本篇文章要讲的核心概念——变量名。
关于变量名,最常见的误解是「变量是一个装数据的盒子」。这个比喻在 Python 里会把你带偏。更准确的说法是:变量名是一个贴在对象上的标签(name binding),赋值语句做的是「把名字绑定到某个对象」,而不是「把值复制进一个盒子」。理解了这个模型,后面遇到a = b之后改b、is和==的区别、可变默认参数这些坑,就都有了解释。
另一个误解是「变量名必须声明类型」。Python 是动态类型语言,变量名本身没有类型,类型属于对象。同一个名字先后绑定到整数和字符串是完全合法的。
本文先快速过一遍安装要点,再详细讲变量名的构成规则、绑定语义和命名约定。文中写法均以 Python 3 为准;Python 2.7 已于 2020 年 1 月 1 日停止维护,如果看到print "x"这类写法,说明那是旧资料。
一、安装:装完之后确认名字解析
安装本身在前面的文章里讲过,这里只强调与「名字」相关的一点:装完之后,确保python这个名字能被正确解析,否则后面所有示例都无从下手。
# 需要 Python 3.x
python --version
python -c "import sys; print(sys.executable)"第二行会打印解释器的真实路径。如果这个路径里含有WindowsApps,说明你命中的是 Windows 的应用执行别名占位符,不是真解释器。
二、变量名的构成规则
Python 里变量名属于标识符(identifier),必须满足:
- 首字符是字母或下划线,不能是数字;
- 其余字符可以是字母、数字或下划线;
- 区分大小写,
name、Name、NAME是三个不同的名字; - 不能与关键字(keyword)重名。
# 适用于 Python 3.8+
name = "ok" # 合法
_name = "ok" # 合法,前导下划线有约定含义
name2 = "ok" # 合法,数字可以出现在非首位
用户 = "张三" # Python 3 中合法:标识符支持 Unicode 字母
2name = "bad" # SyntaxError
class = "bad" # SyntaxError:class 是关键字第四行值得说明:Python 3 的标识符按 Unicode 规则识别(PEP 3131),所以中文等非 ASCII 字母在语法上是允许的。允许不等于推荐——混用中英文标识符会让代码难以搜索和维护,除非有充分理由,否则还是用英文。
想程序化地判断一个字符串能不能当变量名,标准库提供了工具:
# 适用于 Python 3.9+
import keyword
print(keyword.iskeyword("class")) # True
print("class".isidentifier()) # True:语法上是合法标识符
print("2a".isidentifier()) # False
print(keyword.kwlist[:5]) # 前几个关键字
print(keyword.softkwlist) # 软关键字,需要 Python 3.9+注意isidentifier()只判断形状是否合法,它不管这个形状是不是关键字。所以"class".isidentifier()返回True,而class其实是不能用的。两者要配合keyword.iskeyword()一起判断。
「软关键字」(soft keyword)是另一类特殊名字,比如match、case、type、_。它们只在特定语法位置被当作关键字,平时可以正常当变量名用。
三、赋值做了什么:名字绑定而非装箱
# 适用于 Python 3.8+
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4]
print(a is b) # Trueb = a没有复制列表,只是让b和a指向同一个列表对象。改其中一个名字所指向的对象内容,通过另一个名字看也变了。
要判断两个名字是不是指向同一个对象,用is;要判断两个对象的值是否相等,用==。
# 适用于 Python 3.8+
x = [1, 2, 3]
y = [1, 2, 3]
print(x == y) # True:值相等
print(x is y) # False:不是同一个对象至于小数和字符串,is的结果涉及实现细节,不能当作值比较来用。CPython 会缓存 -5 到 256 的小整数,所以两个100常常is为真,两个1000则不一定——这是实现细节,不要依赖。
赋值还有几种便捷写法:
# 适用于 Python 3.8+
p, q = 1, 2 # 并行赋值
p, q = q, p # 交换,右边先构造元组
first, *rest = [1, 2, 3, 4] # 星号收集
print(first, rest) # 1 [2, 3, 4]
head, tail = "ab" # 序列解包,长度必须匹配最后一行如果长度不匹配会抛ValueError,这是解包最常见的报错来源。
四、命名约定
语法规则只管「能不能用」,命名约定管「该不该这么用」。Python 社区的默认约定写在 PEP 8 里。
| 对象 | 约定 | 示例 |
|---|
| 变量、函数、模块 | 小写 + 下划线(snake_case) | user_name、parse_file |
| 类 | 每个单词首字母大写(CapWords) | HttpClient |
| 常量 | 全大写 + 下划线 | MAX_RETRY |
| 内部使用 | 单个前导下划线 | _cache |
| 与关键字冲突时 | 尾部加下划线 | class_、id_ |
| 特殊方法 | 前后双下划线 | __init__、__len__ |
几个约定的实际含义:
- 单个前导下划线表示「这是内部实现」,
from module import *时会跳过它。它是约定,不是访问控制。 - 单个尾随下划线是避开关键字的标准技巧,比自造缩写更好读。
- 双前导下划线(名字修饰,name mangling)在类体里会被解释器改写成带类名前缀的形式,用于避免子类意外覆盖。它不加密,也不是访问控制。
- 前后双下划线的名字由 Python 保留,自己不要随意发明这类名字。
五、不要覆盖内置名
Python 有一批内置名字,比如list、dict、str、id、type、sum、input。它们不是关键字,语法上可以被覆盖,但覆盖之后会出大问题。
# 适用于 Python 3.8+
# ❌ 不要把内置名当变量
list = [1, 2, 3]
# 之后再想用 list(...) 构造列表就会被这个变量挡住# 适用于 Python 3.8+
# ✅ 换个名字
numbers = [1, 2, 3]
total = sum(numbers)同样地,id、type、str、open都不要拿来当变量名或函数名。
常见坑点
- 以为
b = a是复制
❌ 写b = a后修改b,发现a也变了,怀疑是「bug」。
✅ 这是名字绑定:两个名字指向同一个对象。要复制用a.copy()(浅拷贝)或copy.deepcopy(a)(深拷贝)。
- 用
is比较数值
❌ 写if score is 100:,小数值时碰巧成立,换成大数值或浮点数就失灵。
✅is只用来判断「是不是同一个对象」,数值判断一律用==。
- 变量名前导数字
❌ 写2name = "x",直接SyntaxError。
✅ 数字不能开头,改成name2或second_name。
- 变量名与关键字同名
❌ 写class = "A"、lambda = 1,报语法错误。
✅ 换成cls、class_。如果只是想检查某个词是不是关键字,用keyword.iskeyword()。
- 覆盖内置名
❌ 写list = [...]或id = 5,之后的代码里list、id全都失灵。
✅ 换词汇,比如numbers、record_id。命名前先在心里过一遍「这个名字是不是内置的」。
- 大小写混用导致两个名字
❌ 定义Total = 0,后来又写total += 1,报NameError。
✅ 统一命名风格;常量用全大写,变量用全小写加下划线。
- 误以为中文变量名不能用
❌ 看到报错就断定「Python 不支持中文变量名」。
✅ Python 3 支持 Unicode 标识符,中文名语法上合法。但为了可维护性,默认还是用英文命名。
- 认为前导下划线能阻止访问
❌ 写self._secret以为外部访问不到。
✅ 下划线是约定,外部依然能访问。要严格限制用属性封装,双前导下划线也只是名字修饰,不是访问控制。
总结
| 问题 | 结论 |
|---|
| 变量名能包含什么 | 字母、数字、下划线;首字符不能是数字 |
| 能是关键字吗 | 不能 |
| 区分大小写吗 | 区分 |
| 变量有类型吗 | 没有,类型属于对象 |
b = a是复制吗 | 不是,是让两个名字指向同一对象 |
| 怎么比较 | 值用==,同一性用is |
| 命名风格 | 变量函数snake_case,类CapWords,常量UPPER_CASE |
安装解决「有没有解释器」,变量名解决「怎么给数据起名字」。把变量名理解成标签而不是盒子,是读懂 Python 赋值、传参、拷贝这一整套行为的第一步。