别再误解了!Python变量是便利贴,不是盒子

50 阅读4分钟

兄弟们问你们个问题:你是不是也曾经这样理解Python变量:把一个值"装进"一个叫变量的盒子里?比如x = 10就像是把数字10放进了标注为x的盒子中。我曾经也是这么理解的。

但是如果我告诉你这种理解完全错了,你会不会感到惊讶?我估计你不会感到惊讶,你可能会答复我:会用就行了呗,我理解他干甚?但是这个还得说一下,既然选择了它,就得了解它的全部不是?

接下来我们就一起回忆回忆这个知识点吧!

便利贴,而不是盒子

Python变量实际上更像是便利贴,而不是盒子。当你写x = 10时,你并不是在创建一个叫做x的盒子并把10放进去,而是在创建一个值为10的对象,然后贴上一张写着"x"的便利贴。

这个概念可能有点抽象,让我用图解来解释:

内存中的对象:  10
               ^
               |
变量便利贴:    x

赋值操作的真实含义

当我们执行x = 10时,实际上发生了两件事:

  • Python在内存中创建了一个整数对象10
  • 创建了一个名为x的变量,它指向(引用)这个对象

这才是Python变量工作的真实方式——变量只是对象的标签或名称,而不是存储数据的容器。

一个例子看清本质

让我们通过一个简单例子来验证这个观点:

x = 10
y = x
x = 20

print(x)  # 输出:20
print(y)  # 输出:10

如果变量是盒子,那么当我们将x赋值给y时,应该是将x盒子里的内容复制到y盒子中。但事实上,y的值并没有随x的改变而改变,这说明我们的"便利贴"模型更准确。

实际的内存情况是这样的:

  1. x = 10:创建对象10,x贴在上面
  2. y = x:y也贴在同一对象10上
  3. x = 20:创建新对象20,x从10上撕下贴到20上,y仍然贴在10上

可变与不可变对象的差异

上面的例子中我们使用的是整数(不可变对象),那么对于可变对象(如列表)呢?

image.png

啊哈!这次b的值随着a的改变而改变了。这是为什么?

因为当我们执行b = a时,我们并没有创建列表的副本,而是让b和a贴在了同一个列表对象上。当我们修改这个列表时,通过a或b都能看到变化。

内存示意图:

初始状态:
a[1, 2, 3]b

执行a.append(4)后:
a[1, 2, 3, 4]b

函数参数传递的真相

理解了变量是便利贴而不是盒子,我们就能真正理解Python的函数参数传递机制。

Python中的参数传递既不是传值也不是传引用,而是传对象引用。也就是说,函数参数接收的是原始对象的便利贴,而不是它的副本。

def modify_list(lst):
    lst.append(4)

my_list = [1, 2, 3]
modify_list(my_list)
print(my_list)  # 输出:[1, 2, 3, 4]

在这个例子中,lst参数和my_list变量都贴在同一列表对象上,所以通过任一便利贴做的修改都会反映到另一便利贴上。

但是,如果我们在函数内部重新赋值呢?

def try_modify_number(num):
    num = 10  # 创建新对象,将num贴上去

x = 5
try_modify_number(x)
print(x)  # 输出:5

这里x的值没有改变,因为函数内部的num = 10创建了一个新对象10,并将num从原来的对象5上撕下贴到了新对象上。原始的x便利贴仍然贴在5上。

实际编程中的应用

理解变量是便利贴而不是盒子,对于避免常见编程错误至关重要:

  1. 浅拷贝与深拷贝:当你需要复制可变对象而不是共享引用时,必须明确使用拷贝操作
import copy

original = [1, [2, 3]]
shallow_copy = copy.copy(original)  # 浅拷贝
deep_copy = copy.deepcopy(original)  # 深拷贝
  1. 比较对象is比较两个变量是否贴在同一对象上,==比较对象的值是否相等
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)  # True,值相等
print(a is b)  # False,不同对象
print(a is c)  # True,同一对象
  1. 内存管理:理解何时对象会被垃圾回收(当没有任何便利贴贴在上面时)

好了,这次的内容就呈现这么多了,是不是对Python变量有了全新的认识?

记着!去动动手,别光看!