Day1 Python的浅拷贝和深拷贝

0 阅读2分钟

Python的变量本质

Python中几种最常用的基本类型:

age = 25          # int,整数
price = 9.9       # float,浮点数
name = "Alice"    # str,字符串
is_ok = True      # bool,布尔值
result = None     # NoneType,表示"空"或"未赋值"

Python 是动态类型语言,变量本身没有类型,类型是对象的属性。即,动态类型意味着变量本身没有类型,类型是附着在值上的。同一个变量可以先指向整数,再指向字符串,Python 不会报错:

# 动态类型的体现:同一变量可指向不同类型
x = 10          # x 是 int
x = "now str"   # x 现在指向 str 对象,原 int 被回收

这与 Java、C++ 不同。Python 的变量更像是一个"标签",贴在对象上,而不是一个固定类型的盒子。

可变对象 vs 不可变对象

Python 的对象分两类:

  • 不可变(immutable)intfloatstrtuplebool
  • 可变(mutable)listdictset

"可变"的意思是:对象创建后,内容可以被修改,且内存地址不变:

a = [1, 2, 3]
# 使用id()函数返回对象的唯一标识符(内存地址)
print(id(a))   
# 假设是 140234567
a.append(4)
print(id(a))   
# 还是 140234567,地址没变,但内容变了

而不可变对象一旦"修改",实际上是创建了一个新对象:

s = "hello"
print(id(s))   # 假设是 205290313838
s = s + " world"
print(id(s))   # 变成了 205290245440,s现在指向一个全新的字符串对象

函数参数的 引用传递

Python 传参传的是"对象的引用"。如果你传进去的是可变对象,函数内部对它的修改会直接反映到外部。例如:

def add_default_field(records):
    for record in records:
        record["verified"] = False  # 直接修改了 dict
    return records

raw_data = [{"name": "Alice"}, {"name": "Bob"}]
processed = add_default_field(raw_data)

print(raw_data)
# [{'name': 'Alice', 'verified': False}, {'name': 'Bob', 'verified': False}]

注意:raw_data 被修改了。你没有在函数里写任何"修改原始数据"的意图,但它就是发生了。如果后续代码还依赖 raw_data 保持原样,就会出现难以追踪的数据污染问题。

浅拷贝 VS 深拷贝

Python 提供两种拷贝方式:

  • 浅拷贝- copy.copy():复制外层容器,但内部的对象仍然共享引用,修改内层对象会影响原对象。
import copy

original = [[1, 2], [3, 4]]
shallow = copy.copy(original)

shallow[0].append(99)
print(original)  # [[1, 2, 99], [3, 4]],内层列表还是同一个
  • 深拷贝- copy.deepcopy():递归复制所有层级的对象,完全独立,修改任何层都不会影响原对象。
deep = copy.deepcopy(original)
deep[0].append(99)
print(original)  # [[1, 2], [3, 4]],不受影响

总结

可变对象不可变对象
代表类型list、dict、setstr、tuple、int
修改行为原地修改,地址不变产生新对象
函数传参风险内部修改影响外部无此问题