当前位置:首页>python>看看Python的内存管理(一):万物皆对象

看看Python的内存管理(一):万物皆对象

  • 2026-09-02 20:34:09
看看Python的内存管理(一):万物皆对象
用过Java的人可能都了解过它的内存管理机制,今天我们也来看看Python的内存管理,首先让我们从最基础的对象入手,了解==、is和id()。
==是我们最常用的运算符,它的作用是检查两个对象包含的值是否一致,当我们写a==b时,python会比较两个对象的实际数据,这是一种深度比较,有时候还会调用对象的特殊方法__eq__(),让我们看个简单的例子:
# == 比较的是对象的值a = [1, 2, 3]b = [1, 2, 3]print(a == b)  # True - 两个列表的内容完全相同
在底层来看,==可能触发更复杂的操作,对于列表,它会逐一比较每个元素的值,对于自定义对象,他会调用我没重写的__eq__方法,实现自定义的相等逻辑。同样,我们看个自定义对象的例子:
class SensorValue:    def __init__(self, value, tolerance):        self.value = int(value)  # 传感器数值        self.tolerance = int(tolerance)  # 误差范围    def __eq__(self, other):        # 自定义相等逻辑:两个数值在彼此的误差范围内,就认为相等        return abs(self.value - other.value) <= min(            self.tolerance,            other.tolerance        )v1 = SensorValue(100, 3)  # 数值100,误差±3v2 = SensorValue(97, 5)   # 数值97,误差±5v3 = SensorValue(96, 10)  # 数值96,误差±10print(f"v1 == v2: {v1 == v2}")  # True - 97在100±3范围内,100也在97±5范围内print(f"v1 == v3: {v1 == v3}")  # False - 96不在100±3范围内
和==不同,is检查的是对象身份,也就是两个变量是否只想内存中的同一个对象,而不是判断值是否相同。
# `is` 比较的是对象身份(是否指向同一个内存地址)a = [1, 2, 3]b = a  # b 直接引用a指向的列表对象,没有创建新对象c = [1, 2, 3]  # 重新创建一个列表,内容和a相同,但内存地址不同print(a is b)  # True - a和b指向同一个对象print(a is c)  # False - a和c值相同,但指向不同对象
它的逻辑是,当我们写a is b时,Python会直接比较两个对象的内存地址,而不做任何复杂的数值比对,所以在判断None、True 、False时,推荐使用is 而不是 ==,原因是None、True、False都是Python中的“单例对象”,整个程序运行期间,它们只会存在一个实例,所以用is更高效。
id()函数的作用是返回一个对象的虚拟内存地址,以整数形式表示。这个整数,就是is运算符底层用来比较的指针值,还是用例子来看:
a = [1, 2, 3]b = aprint(f"{a is b=}")  # True - 指向同一个对象print(f"{id(a) == id(b)=}")  # True - 内存地址相同,等价于 a is bprint(f"{id(a)=}")  # 输出a的内存地址(整数形式)print(f"id(a)=0x{id(a):x}")  # 将内存地址转为十六进制(更符合我们对地址的认知)
接下来我们用id()做几个小的测试,看看Python的内存管理
x = 4y = 2**2  # 2的平方,结果也是4print(f"{x is y=}")  # True - 居然是同一个对象!print(f"{id(x)=}")print(f"{id(y)=}")  # 和x的内存地址完全相同
上面的代码是否发现了个问题?x和y指向了同一个对象,这是因为Python为了优化性能,会对小整数(-5~256)进行“缓存”,将它们作为单例存储到内存中。那如果我们超出这个范围会怎样呢?
x, y = 2*123, 2**123  # 2*123=246,2**123是一个大整数print(f"{x is y=}")  # False - 不同对象!print(f"{hex(id(x))=}")  # 十六进制内存地址print(f"{hex(id(y))=}")  # 和x的地址不同
很明显,不在这个范围的大整数没有缓存机制,每次都会创建一个新的整数对象。
接下来我们再看一种情况,连续赋值:
x = y = 2**123  # 连续赋值,计算一次2**123print(f"{x is y=}")  # True!print(f"{hex(id(x))=}") print(f"{hex(id(y))=}")  # 和x的地址相同
这次在不在意料之中呢?没存这次它们又指向同一块内存了......
上面我都没有贴输出结果,有兴趣的不妨自己动手看看输出结论吧,下次见。

最新文章

随机文章