x = 10print(f"x 的值:{x}")
print(f"x 的内存地址(id):{id(x)}") # 输出示例:140703383430720
y = x
print(f"y 的值:{y}")
print(f"y 的内存地址:{id(y)}") # 与 x 的 id 完全相同:140703383430720print(f"x 和 y 是否是同一个对象?{x is y}") # 输出:True
结论:y = x 是将 x 的内存地址拷贝给 y,而非拷贝值,因此 x 和 y 指向同一个对象。
1.2 Python 的堆和栈 —— 和 C/C++ 完全不同!
在 C/C++ 中,堆和栈的区别比较明确:
栈:存储局部变量、函数参数,由操作系统自动分配/释放,速度快;
堆:存储动态分配的对象,由程序员手动管理,速度慢。
但Python 的内存模型是'栈存引用,堆存对象':
所有 Python 对象(整数、字符串、列表、字典等)都存储在堆上;
栈上只存储对象的内存地址(引用/指针);
Python 的自动垃圾回收(GC)机制统一管理堆上对象的生命周期,程序员无需手动管理。
代码验证:
deffunc():
a = [1,2,3] # 栈上存储 a 指向的内存地址,堆上存储 [1,2,3] 对象print(f"函数内 a 的地址:{id(a)}") # 示例:140703383221696
func() # 函数执行完毕后,栈上的 a 被销毁,但堆上的 [1,2,3] 对象若没有其他引用,会被 GC 回收
a = [1,2,3] # 堆上再次创建 [1,2,3] 对象,但地址与函数内不同print(f"函数外 a 的地址:{id(a)}") # 示例:140703383221760
x = 256
y = 256print(f"x 的地址:{id(x)}") # 示例:140703383430720print(f"y 的地址:{id(y)}") # 与 x 相同:140703383430720print(f"x is y:{x is y}") # True
x = 257
y = 257print(f"x 的地址:{id(x)}") # 示例:140703383430752print(f"y 的地址:{id(y)}") # 示例:140703383430784(不同地址)print(f"x is y:{x is y}") # False(257 不在小整数池内)
字符串驻留
Python 会对满足条件的字符串进行'驻留'(缓存),避免重复创建。条件是:
字符串仅包含字母、数字、下划线;
字符串长度一般不超过 20 个字符。
代码验证:
x = "abc123"
y = "abc123"print(f"x is y:{x is y}") # True(满足驻留条件)
x = "abc 123"# 包含空格,不满足驻留条件
y = "abc 123"print(f"x is y:{x is y}") # False
注意:不要用 is 比较字符串的相等性,永远用 ==—— 字符串驻留的规则复杂且依赖 Python 版本,无法保证。
二、可变类型 VS 不可变类型 ——Python 最核心的特性
2.1 定义与分类
不可变类型:对象创建后,值无法修改,修改时会创建新的对象,原对象的内存地址不变;
可变类型:对象创建后,值可以修改,修改时不会创建新的对象,原对象的内存地址不变。
分类表:
类型
可变 / 不可变
示例
int
不可变
10
float
不可变
3.14
str
不可变
"hello"
tuple
不可变
(1,2)
bool
不可变
True
None
不可变
None
list
可变
[1,2,3]
dict
可变
{"name": "张三"}
set
可变
{1,2,3}
2.2 不可变类型:修改即创建新对象
代码验证(int 类型):
x = 10print(f"x=10 的地址:{id(x)}") # 示例:140703383430720
x += 5# 修改不可变类型,创建新对象print(f"x=15 的地址:{id(x)}") # 示例:140703383430880(新地址)print(f"x 的当前值:{x}") # 15
代码验证(str 类型):
s = "hello"print(f"s='hello'的地址:{id(s)}") # 示例:140703383221696
s += " world"# 创建新字符串对象print(f"s='hello world'的地址:{id(s)}") # 示例:140703383221760(新地址)
代码验证(tuple 类型):
t = (1,2)
print(f"t=(1,2)的地址:{id(t)}") # 示例:140703383221824# t[0] = 3 → 报错:TypeError: 'tuple' object does not support item assignment(不可修改)
defmodify_list(lst):
lst.append(4) # 修改原对象
a = [1,2,3]
modify_list(a)
print(f"外部 a 的内容:{a}") # [1,2,3,4](被修改了!)
代码验证(不可变类型):
defmodify_int(x):
x += 5# 创建新对象,不会影响外部
b = 10
modify_int(b)
print(f"外部 b 的内容:{b}") # 10(未被修改)
避坑指南:若不想让函数修改外部可变对象,需在函数内部拷贝参数。
3.4 值相等(==)VS 身份相等(is)的区别
==:值相等—— 比较两个对象的内容是否相同;
is:身份相等—— 比较两个对象的内存地址是否相同。
代码验证:
a = [1,2,3]
b = [1,2,3]
print(f"a == b:{a == b}") # True(内容相同)print(f"a is b:{a is b}") # False(不同内存地址)
x = None
y = Noneprint(f"x is y:{x is y}") # True(None 是单例对象,只有一个内存地址)
import copy
a = [[1,2], [3,4]]
c = copy.deepcopy(a) # 深拷贝print(f"a 的地址:{id(a)}") # 示例:140703383222208print(f"c 的地址:{id(c)}") # 示例:140703383222400(新地址)print(f"a is c:{a is c}") # False# 嵌套对象也被拷贝了print(f"a[0] 的地址:{id(a[0])}") # 示例:140703383222336print(f"c[0] 的地址:{id(c[0])}") # 示例:140703383222464(新地址)print(f"a[0] is c[0]:{a[0] is c[0]}") # False# 修改 c 的嵌套对象,a 不变
c[0][0] = 1print(f"a 的内容:{a}") # [[5,2], [3,4]](a 未被修改)print(f"c 的内容:{c}") # [[1,2], [3,4]]
4.4 哪些操作是浅拷贝?
除了 copy.copy(),Python 还有以下内置的浅拷贝操作:
列表的 copy() 方法:a.copy();
字典的 copy() 方法:d.copy();
切片操作:a[:];
构造函数:list(a)、dict(d)、set(s)。
代码验证:
a = [[1,2], [3,4]]
b = a.copy() # 浅拷贝
c = a[:] # 浅拷贝
d = list(a) # 浅拷贝print(f"b[0] is a[0]:{b[0] is a[0]}") # Trueprint(f"c[0] is a[0]:{c[0] is a[0]}") # Trueprint(f"d[0] is a[0]:{d[0] is a[0]}") # True