知了常识站
白蓝主题五 · 清爽阅读
首页  > 电脑基础

Python内存管理入门:让你的程序更省内存

Python中的内存是怎么回事

写Python代码时,很多人只关心功能能不能跑通,很少去想变量存哪儿了、内存会不会爆。但如果你处理的是大量数据,或者程序跑着跑着越来越慢,那可能就得看看内存这块儿的问题了。

Python作为一门高级语言,自动帮我们做了很多内存管理的事。比如你创建一个变量,它就分配内存;这个变量不用了,它就回收。这套机制叫“垃圾回收”,背后靠的是引用计数和循环垃圾检测。

引用计数:谁在用这块内存

每个对象在Python中都有一个“被引用”的次数。比如你把一个列表赋给两个变量,它的引用数就是2。

a = [1, 2, 3]
b = a
print(id(a), id(b)) # 输出相同地址,说明是同一个对象

当你把a设为None,或者del a,引用数就减1。等到引用数变成0,这块内存就会立刻被释放。这是Python内存回收的最基本方式。

小心循环引用

有时候两个对象互相引用,即使外部没人用了,它们的引用数也不为0。比如:

class Node:
def __init__(self, name):
self.name = name
self.parent = None
self.children = []

a = Node('A')
b = Node('B')
a.children.append(b)
b.parent = a

这时候如果删掉a和b,它们其实还互相指着,引用数不归零。Python的引用计数机制搞不定这种情况,得靠另一个机制——循环垃圾回收器来定期清理。

怎么减少内存占用

有些操作看着简单,却悄悄吃掉大量内存。比如读一个大文件:

with open('big_file.txt', 'r') as f:
lines = f.readlines() # 一次性全读进内存

如果文件有几百MB,这行代码可能直接让程序卡住。更好的做法是逐行读:

with open('big_file.txt', 'r') as f:
for line in f: # 每次只加载一行
process(line)

再比如,生成大量临时数据时,可以用生成器代替列表。列表推导式写起来爽,但全存进内存:

nums = [x**2 for x in range(1000000)]  # 占用大

换成生成器表达式,用多少算多少:

nums = (x**2 for x in range(1000000))  # 几乎不占内存

手动干预也是可以的

虽然Python自动管理内存,但你也可以主动参与。比如告诉垃圾回收器赶紧干活:

import gc

del large_data # 删除大对象
gc.collect() # 立即触发垃圾回收

这在处理完一批大数据后特别有用,能快速释放资源。

还有个小技巧,用__slots__可以限制类的属性,减少每个实例的内存开销。适合要创建成千上万个对象的情况。

class Point:
__slots__ = ['x', 'y'] # 只允许这两个属性

def __init__(self, x, y):
self.x = x
self.y = y

这样每个实例不再用字典存属性,省内存也更快。

观察内存使用情况

想知道你的代码到底占了多少内存?可以用memory_profiler这类工具。装好之后加个装饰器就能看:

@profile
def my_func():
a = [i for i in range(100000)]
b = [i**2 for i in a]

运行时加上 -m memory_profiler 参数,就能看到每行代码的内存消耗。

平时写脚本别太放肆,尤其在服务器或树莓派这种资源有限的设备上。一个小优化,可能就让程序从卡顿变流畅。