Python中+=和普通+的区别

在 Python 开发(特别是使用 PyTorch 进行深度学习训练)时,许多人认为 a = a + ba += b 是完全等价的。但在底层内存管理和性能表现上,它们有着本质的区别。理解这一点,对于优化数据处理管道和避免显存碎片化至关重要。

先看结论:

  • a + b创建新对象,不会修改原变量 a,只是返回相加结果
  • a += b原地修改(增量赋值),优先修改变量本身;分可变数据类型不可变数据类型两种情况

一、可变数据类型与不可变数据类型

Python3 中有六个标准的数据类型:

  1. Number(数字)
  2. String(字符串)
  3. List(列表)
  4. Tuple(元组)
  5. Set(集合)
  6. Dictionary(字典)

其中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

1.1 不可变数据类型

当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = 10
b = 20
print(id(a))
a = a + b
print(id(a), a)
a = 40
print(id(a))
a += b
print(id(a), a)

""" output:
2347873600016
2347873600656 30
2347873600976
2347873601616 60
"""

1.2 可变数据类型

当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型。

1
2
3
4
5
6
7
8
9
10
11
list = ["张三", "李四", "王五"]
print(id(list))
list[0] = "阿三"
print(id(list))

""" output:
list = ["张三", "李四", "王五"]
print(id(list), list)
list[0] = "阿三"
print(id(list), list)
"""

二、+=和普通加法

刚才研究了可变数据类型和不可变数据类型,下面回到加法的讨论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def change_value(data):
data += 2 # 原地运算,修改原tensor内存
print(f"change_value:id(data):{id(data)}, data:{data}")

def not_change_value(data):
data = data + 2 # 创建新tensor,仅修改局部变量data的指向
print(f"not_change_value, id(data):{id(data)},data:{data}")
return data

x = torch.tensor([1,2])
print(f"original, id(x):{id(x)},x:{x}")

change_value(x)
y = not_change_value(x)
print(f"after not_change_value, id(x):{id(x)},x:{x},id(y):{id(y)},y:{y}")

""" output:
original, id(x):2667632061840,x:tensor([1, 2])
change_value:id(data):2667632061840, data:tensor([3, 4])
not_change_value, id(data):2667748901088,data:tensor([5, 6])
after not_change_value, id(x):2667632061840,x:tensor([3, 4]),id(y):2667748901088,y:tensor([5, 6])
"""

这里要注意一点:普通数字/字符串是不可变对象,而 torch.Tensor可变容器对象,支持原地运算 +=

  • tensor += n:原地自增,不新建张量
  • tensor = tensor + n:先计算 tensor + n 得到新张量,再把局部变量 data 指向新张量,和外部原张量断开关联

由此可以看出,data = data + 2 这里其实是生成了一个全新的 tensor,然后将局部变量 data 重新绑定到这个新张量上,而原来外部 x 指向的张量没有发生变化,依旧是 [3, 4]

  1. data += 2 对应方法 __iadd__(原地加法)

Tensor 实现了原地加法,逻辑:

直接在当前张量内存中修改数值,不分配新内存

适用于 list、tensor、array 这类可变对象。

  1. data = data + 2 对应方法 __add__(普通加法)

普通加法一定会创建新对象,流程:

  1. 计算 data + 2,开辟新内存存储结果
  2. 局部变量 data 放弃原有指向,绑定到新张量
  3. 函数外的原变量 x 不受任何影响

三、应用

为了方便进行原位操作,PyTorch 中的函数可以在调用之后加下划线,强调这是进行原位操作(在模型训练中经常用到的 w.grad.zero() 即为原位更新,注意 _ 是放在()之前的),简单得用上述例子进行实现,上述操作也可以这样使用:

1
2
3
4
5
6
7
8
9
10
a = torch.ones((1, ))
print(id(a), a)

a.add_(torch.ones((1,)))
print(id(a),a)

""" output:
2018457103440 tensor([1.])
2018457103440 tensor([2.])
"""

需要注意的是,此种用下划线 _ 的原位操作方法在面对叶子节点的时候不可以使用。因为叶子节点在迭代过程中需要被用到其他量或梯度的求解,不允许被更改,如果轻易被修改,会导致其他的值计算错误。


Python中+=和普通+的区别
http://example.com/2026/06/13/Pythonh中-和普通-的区别/
作者
Yu xin
发布于
2026年6月13日
许可协议