python复习--对象相关--对象生命周期

一. 一句话总览版

Python 对象的生命周期是:
创建 → 被引用 → 引用变化 → 不可达 → 回收

Python 只关心“引用”,不关心“是否使用”。

二、Python 世界里最重要的 3 个概念

1.对象(Object)

  • 真正存在于内存中的东西
  • 例如:整数、字符串、函数、类、列表……

2.名字(Name / 变量名)

  • 只是一个引用标签
  • 本身不存数据

3.引用(Reference)

  • 名字 / 容器 / 属性 → 对象 的指向关系
    名字 ──▶ 对象
    名字 ≠ 对象

三、对象生命周期第 1 阶段:创建(Creation)

  • 对象只会在执行“创建语句”时创建。

1. 常见的创建方式

绑定名字

x ──▶ <listobject>

名字 x 指向这个 list 对象
引用计数 = 1Python 不会“先创建一个没有引用的对象,再找名字给它”
这两步在语义上是原子完成的。

创建对象

[]

在内存中创建一个 list 对象
对象此时 必须被某个引用接住

逐字拆解 x = [] 到“解释器视角”

x =[]

在这一行里,发生了 两个动作(但对你来说像一步):

示例

a =10# 创建 int 对象 b =[]# 创建 list 对象 c ={}# 创建 dict 对象 d =lambda x: x # 创建函数对象

[] 是对象本身(一个 list 对象)
b 不是对象,只是一个名字(引用)
b = [] 的含义是:
创建一个 list 对象,然后让名字 b 指向它

2. def / class 也是“创建对象”

执行到这行时:
创建函数 / 类对象
绑定名字

语法创建的对象绑定的名字
def f()函数对象f
class A类对象A

示例

deff():passclassA:pass

四、对象生命周期第 2 阶段:被引用(Alive)

1.示例

  • 内存关系:f ──▶ <function f>
  • 此时:
    引用数 ≥ 1
    对象是“活着的”
    GC 不会碰它

示例

deff():pass

2.引用是怎么变化的?(这是核心)

引用 -1 的情况

b =None

a ───▶ []
引用数 = 1(对象仍活着)

引用 +1 的情况

a =[] b = a 

a ─┐
├──▶ []
b ─┘
引用数 = 2

五、对象生命周期第 3 阶段:不可达(Unreachable)

1. 关键规则

当一个对象没有任何引用指向它时,它就“不可达”

示例

f =[] f =None

执行过程:
f = []:创建 list,对象被 f 引用 (引用=1)
f = None:解除引用 (引用=0)执行结束后:
没有任何名字指向该 list
引用计数 = 0
对象不可达
等待 / 立即回收(CPython)

2. 补充(需要注意)

不可达 ≠ 立刻销毁(在所有实现中)

CPython:引用计数 → 通常立刻回收
其他实现(PyPy):可能延迟回收

六、对象生命周期第 4 阶段:回收(Garbage Collection)

  • Python 的 GC 两层机制

1. 第一层:引用计数(最核心、最常用)

  • CPython 中,每个对象都有 refcount,这是 CPython 的基础机制。

什么是引用计数?
每个对象内部都有一个 refcount
每多一个引用,计数 +1
每少一个引用,计数 -1
当 refcount == 0 → 对象立刻销毁(大多数情况)
示例:

a =[] a =None

a = [] → 引用数 = 1
a = None → 引用数 = 0 → 回收

2. 第二层:循环垃圾回收(Cycle GC)

  • 为了解决循环引用,Python 引入了第二层机制。

即使这样:

a =None b =None

你会以为对象可以被回收,但实际上:a 和 b 互相引用
各自的 refcount 都不为 0
引用计数机制 无法回收它们

循环引用问题(引用计数解决不了)

a =[] b =[] a.append(b) b.append(a)

此时结构是:

 a → b ↑ ↓ └───┘ 

循环 GC 的核心思想
只要对象组“从程序中不可达”,就应该被回收
不管它们内部怎么互相引用。

a =[] b =[] a.append(b) b.append(a) a =None b =None
  • 此时:
    程序中已经没有任何变量能访问这两个列表
    虽然它们内部还互相引用
    循环 GC 会发现它们不可达,并最终回收

3. 表格记忆

机制解决什么特点
引用计数普通对象回收快、实时
循环 GC循环引用定期扫描、补救机制

99% 情况靠引用计数
1% 循环引用靠 GC 扫描

七、一个“完整生命周期时间线”示例(重点)

1. 示例

defmake(): x =[]return x a = make() b = a a =None b =None

执行过程:

时刻状态
x = []创建 list,对象一出生就被 x 引用,引用数 = 1
return x返回引用,返回的是对象的引用,不是新对象
a = make()a 指向 list,引用数 = 1
b = a引用 +1
a = None引用 -1
b = None引用 -1,引用 = 0 → 回收

这里的 a 和 b 这两个名字,绑定到了同一个 list 对象上

八、装饰器中的对象生命周期

1. 示例一:装饰器保存原函数引用

defdecorator(func):defwrapper(): func()return wrapper @decoratordeff():print("hi")

装饰器在函数定义阶段执行,等价于:

deff():print("hi")defdecorator(func):defwrapper(): func()return wrapper f = decorator(f)

逐步执行过程(对象 + 引用)

  1. 原函数为什么没被回收?
    因为:
    wrapper.closure → func → f_original
    虽然名字 f 不再指向原函数
    但 wrapper 闭包仍然引用它
    f_original 仍然存活
    总结一句话, 装饰器通过闭包,延长了原函数对象的生命周期

返回 wrapper 并重新绑定名字

f = wrapper 

原来的 f 名字不再指向 f_original
f → wrapper

创建 wrapper 函数对象

defwrapper(): func()

创建 wrapper 函数对象
wrapper 闭包捕获了 func
func → f_original

调用装饰器

decorator(f_original)

func 参数 → 引用 f_original

创建原函数对象

deff():print("hi")

创建 函数对象 f_original
名字 f → 绑定到 f_original

2. 示例二:装饰器不保存原函数引用

defdecorator(func):returnlambda:print("hi")@decoratordeff():print("hi")

等价于:

deff():print("hi")defdecorator(func):returnlambda:print("hi") f = decorator(f)

执行过程

  1. 创建原函数对象创建 f_original
    f → f_original
  2. 结果
    f_original 变为不可达对象
    引用计数 = 0
    立即被回收

名字重新绑定

f = lambda_func 

原来的 f 不再指向 f_original
没有任何引用指向 f_original

调用 decorator

decorator(f_original)

但 返回的 lambda 没有引用 func

3. 两种装饰器的“生命周期对比表”

情况原函数是否被保存原函数生命周期
wrapper 使用 func是(闭包)存活
不使用 func立即回收

九、对象“不会被回收”的常见原因

1. 还有全局引用

cache =[] cache.append(obj)

2. 被闭包捕获

defouter(): x =[]definner():return x return inner 

3. 存在循环引用 + del

classA:def__del__(self):pass

可能导致无法回收

十、你现在可以用这段话“完美描述对象生命周期”

Python 对象在执行创建语句时生成,
通过名字、容器或属性被引用;
在运行过程中引用动态变化;
当对象不再被任何引用指向时变为不可达;
随后由引用计数或 GC 回收,生命周期结束。

这是解释器级理解

十一、最终总结(必记)

Python 不关心是否调用
Python 不预测未来代码
Python 只在当前时刻检查引用
对象一旦不可达,永远无法复活

Could not load content