很多新手学 Python 时,脑子里都有这样一个画面:变量是一个盒子,赋值就是把数据"装"进盒子。比如 a = 1,就是把数字 1 装进名叫 a 的盒子里。这个比喻在入门阶段够用,但一旦碰到列表、字典,或者想弄明白"为什么我改了一个变量,另一个也跟着变",这套说法就彻底失灵了。
今天我们用最直白的方式,把 Python 变量的本质一次讲清楚。记住一句话就够了:变量不是盒子,而是贴在对象上的标签。
一、赋值到底发生了什么
当你写下:
a = 100
b = a
Python 真正做的是两件事:先在内存里创建对象 100,再把标签 a 贴上去。执行 b = a 时,并没有复制一个新的 100,而是把 同一个标签 b 也贴到了这个对象上。此时 a 和 b 指向内存里的同一个对象。
可以用 id() 验证:
print(id(a), id(b)) # 两个值完全相同
print(a is b) # True,指向同一个对象
二、变量没有类型,对象才有
Python 是动态类型语言。同一个变量,今天装整数、明天装字符串、后天装函数,完全合法:
x = 42
type(x) # <class 'int'>
x = "hello"
type(x) # <class 'str'>
看到没?x 一直叫 x,但它指向的对象换了。类型属于对象,标签只是名字。这也解释了为什么 Python 不需要像 C 或 Java 那样声明类型——名字无所谓,重要的是名字指向谁。
三、可变与不可变:一切坑的根源
对象分两类:不可变(整数、浮点数、字符串、元组)和可变(列表、字典、集合)。不可变对象一旦创建就不能改;可变对象可以"原地修改"。
s = "abc"
s += "d" # 其实创建了新字符串,s 重新贴标签
lst = [1, 2]
lst.append(3) # 原地修改,列表对象没变
字符串拼接看似"改"了 s,实际是生成新对象再换标签;而 append 是真正在原来的列表上动手脚。区别就在这里。
四、引用与拷贝:多张标签贴同一个对象
当你把列表赋给两个变量,它们指向同一个列表,改一个,另一个也变——因为它们本来就是同一个东西:
a = [1, 2, 3]
b = a # 只是多贴一张标签
b.append(4)
print(a) # [1, 2, 3, 4] 意外吗?
如果确实想复制一份独立的数据,用切片或 copy 模块:
import copy
b = a[:] # 浅拷贝,第一层独立
c = copy.deepcopy(a) # 深拷贝,完全独立
浅拷贝只复制最外层,里面的子列表还是共享的;深拷贝则把整棵树都复制一遍。多数业务场景,先想清楚"我要的是共享还是独立",就不会被别名坑到。
五、三个避坑小提醒
- 不要用可变对象做函数默认参数:
def f(x=[]) 的默认列表被所有调用共享,容易出诡异 bug,改用 x=None。 - == 比内容,is 比身份:比较数值用
==,判断"是不是同一个对象"才用 is。 - 函数传参传的是引用:在函数里修改传入的列表,外面也能看到,这是特性不是 bug,写代码时心里有数即可。
总结一下:Python 的变量是贴在对象上的标签,对象才拥有类型与值;弄明白"标签、对象、可变性"这三者的关系,赋值、传参、拷贝相关的坑就都能一眼看穿。
如果你觉得有用,欢迎点赞、在看、转发给同样在学 Python 的朋友。