变量不是盒子,而是贴在值上面的名字标签。搞清楚这一点,你就不会再被"赋值"和"改值"搞晕。
前面的五篇,我们从元类一路聊到推导式,全是"先有对象,再谈语法"。现在到了入门篇,我们从最基础的地方重新出发。别担心,你已经在前面见过这些概念了,这一篇只是把它们讲得更慢、更细。
你将学到
- 变量在 Python 里到底是什么(名字绑定,不是盒子)
- 动态类型意味着什么,以及它给你的自由与坑
- 五种最常用的类型:
int、float、str、bool、None - 用
type()和isinstance()检查类型 - 变量命名规范、赋值、多重赋值、交换
- 为什么有的值能改、有的值不能改(可变 vs 不可变)
- f-string:把变量塞进文本里的现代写法
前置知识
建议先读 上一篇:推导式与函数式工具,或者干脆把这一篇当作新起点。
变量是什么:名字绑定
很多教材会说"变量是一个盒子,你把值放进去"。这个比喻在 C 语言里勉强成立,但在 Python 里会误导你。
Python 的真相是:值(对象)先存在,变量只是贴在它上面的一个名字标签。
x = 42
这行代码做的是:创建一个整数对象 42,然后让名字 x 指向它。以后你说 x,Python 就顺着标签去找那个对象。
x = 42
y = x # 让 y 也指向同一个 42
x = 100 # 让 x 改指 100,y 不受影响
print(x, y) # 输出: 100 42
注意:x = 100 改的是"x 这个名字指向谁",不是"把 42 这个值改成 100"。42 还是 42。
用一个办法验证"标签"这个说法——用 id() 看对象的内存地址:
a = 42
b = a
print(id(a) == id(b)) # 输出: True,两个名字指向同一个对象
a = 100
print(id(a) == id(b)) # 输出: False,a 换对象了,b 没动
动态类型:名字没有类型,对象才有
在 Python 里,变量本身没有类型。同一个名字,这一秒指向整数,下一秒可以指向字符串:
v = 10 # v 现在指向一个 int
v = "十" # v 现在指向一个 str,完全合法
v = [1, 2, 3] # 又指向一个 list
print(v) # 输出: [1, 2, 3]
这叫动态类型。好处是写起来灵活,坏处是错误可能在运行时才暴露。所以后面进阶篇会教你用类型注解 + mypy 来"提前抓错"。
类型属于对象,不属于名字。 记住这句话,很多疑惑会自动消失。
五种最常用的类型
int:整数
a = 7
b = -3
c = 1000000000000000000 # Python 的整数可以任意大,不会溢出
print(type(a)) # 输出: <class 'int'>
float:浮点数
pi = 3.14159
tiny = 1.5e-3 # 科学计数法,等于 0.0015
print(pi + 1) # 输出: 4.14159
print(type(pi)) # 输出: <class 'float'>
print(0.1 + 0.2) # 输出: 0.30000000000000004(浮点数精度问题,详见常见坑)
str:字符串
name = "Python"
greeting = '你好'
print(type(name)) # 输出: <class 'str'>
print(len(name)) # 输出: 6
bool:布尔值
只有两个值:True 和 False。注意首字母大写。
is_ready = True
is_done = False
print(type(is_ready)) # 输出: <class 'bool'>
print(3 > 2) # 输出: True
None:空值
表示"什么都没有"。它是 NoneType 类型的唯一一个值。
result = None
print(type(result)) # 输出: <class 'NoneType'>
print(result is None) # 输出: True(判断 None 用 is,不用 ==)
type() 与 isinstance()
查类型有两种方式,用途不同。
n = 42
# type():精确判断"是不是恰好是这个类型"
print(type(n)) # 输出: <class 'int'>
print(type(n) is int) # 输出: True
# isinstance():判断"是不是这个类型,或者它的子类"
print(isinstance(n, int)) # 输出: True
print(isinstance(True, int)) # 输出: True(bool 是 int 的子类!)
实践建议:做类型判断时优先用 isinstance(),因为它认子类。type(x) == int 会漏掉 bool 和 int 的自定义子类。
value = True
print(type(value) is int) # 输出: False(bool 不等于 int)
print(isinstance(value, int)) # 输出: True(bool 是 int 的子类)
变量命名规范
规则(写了会报错):
- 只能包含字母、数字、下划线
_ - 不能以数字开头
- 不能是 Python 关键字(如
if、class、for)
约定(不报错,但请遵守):
user_name = "小明" # ✅ 小写下划线(snake_case),推荐
userName = "小明" # ⚠️ 能跑但不是 Python 风格
MAX_SIZE = 100 # ✅ 常量用全大写
_x = 1 # ✅ 前导下划线表示"内部使用"
# ❌ 这些名字虽然合法,但会让你自己和同事难受
x = 3
a1 = 4
data = 5 # 含义不明的短名
list = [1, 2] # 覆盖了内置的 list 函数!
# ✅ 名字就是最好的注释
width = 3
height = 4
area = width * height
赋值、多重赋值、交换
# 普通赋值
a = 1
# 多重赋值:一次给多个名字赋值
x, y, z = 1, 2, 3
print(x, y, z) # 输出: 1 2 3
# 同一个值赋给多个名字
p = q = 0
print(p, q) # 输出: 0 0
# 交换两个变量(Python 独门绝技,无需临时变量)
a, b = 10, 20
a, b = b, a
print(a, b) # 输出: 20 10
上面那句交换,背后的原理就是前面讲的"名字标签":先算出右边的元组 (b, a),再依次贴回左边的名字。
不可变 vs 可变(初识)
同样是"改值",行为却不同:
# int 是不可变的:看起来"改了",其实是换了新对象
n = 10
print(id(n)) # 输出: 某个地址
n = n + 1
print(id(n)) # 输出: 另一个地址(n 换了新对象)
# list 是可变的:原地修改,对象本身没换
nums = [1, 2, 3]
print(id(nums)) # 输出: 某个地址
nums.append(4)
print(id(nums)) # 输出: 同一个地址(对象没换,内容变了)
print(nums) # 输出: [1, 2, 3, 4]
记住:不可变类型(int、float、str、tuple、bool、None)一改就换新对象;可变类型(list、dict、set)可以原地改。 这个区别会在后面讲字典和集合时反复用到。
f-string 初体验
把变量拼进字符串,最现代的写法是 f-string(前缀 f,Python 3.6+):
name = "小明"
age = 18
# ✅ 推荐:可读性最好
print(f"我叫{name},今年{age}岁") # 输出: 我叫小明,今年18岁
# 花括号里可以直接写表达式
print(f"明年我{age + 1}岁") # 输出: 明年我19岁
print(f"名字长度:{len(name)}") # 输出: 名字长度:2
对比一下老写法,你就明白好处了:
# 老写法一:字符串拼接(类型不对就报错)
print("我叫" + name + ",今年" + str(age) + "岁")
# 老写法二:% 格式化
print("我叫%s,今年%d岁" % (name, age))
f-string 的更多格式化技巧,我们会在 26-字符串与格式化 里专门展开。
常见坑
坑一:浮点数不精确
# ❌ 用 == 直接比较浮点数
print(0.1 + 0.2 == 0.3) # 输出: False(它们是 0.30000000000000004)
# ✅ 用一个极小误差比较,或改用 decimal
import math
print(math.isclose(0.1 + 0.2, 0.3)) # 输出: True
坑二:判断 None 用 is 而不是 ==
x = None
# ❌ == 可能被自定义的 __eq__ 影响,语义上也不够准确
if x == None:
pass
# ✅ is 判断的是"是不是同一个对象",这才是你想要的
if x is None:
pass
坑三:变量名覆盖内置函数
# ❌ list 是内置函数,被覆盖后再也调不到它了
list = [1, 2, 3]
# ✅ 换个名字
numbers = [1, 2, 3]
坑四:字符串和数字相加
age = 18
# ❌ TypeError: can only concatenate str (not "int") to str
# print("年龄:" + age)
# ✅ 用 f-string 或先转成 str
print(f"年龄:{age}")
print("年龄:" + str(age))
小结
- 变量是贴在对象上的名字标签,赋值 = 改标签指向,不是改值本身。
- 名字没有类型,类型属于对象;这就是"动态类型"。
- 五种常用类型:
int、float、str、bool、None。 - 查类型用
type()(精确)或isinstance()(认子类,更推荐)。 - 命名用
snake_case,常量全大写,别覆盖内置名。 - 多重赋值和
a, b = b, a交换是 Python 的招牌写法。 - 不可变类型一改就换新对象;可变类型能原地改。
- f-string 是拼字符串的首选:
f"你好{name}"。
延伸阅读
- Python 官方文档:内置类型
- PEP 8 —— 官方的命名与风格指南
上一篇:推导式与函数式工具 · 下一篇:运算符与表达式
文章回复
0 条公开回复