我有一个非常简单的python-3代码,这让我很困惑。
test.py
:
import numpy as np
class PARTICLE:
def __init__(self, PosV = np.zeros(3), Mass=0):
self.posV = PosV
self.mass = Mass
def main():
pL = []
for i in range(10):
p = PARTICLE(Mass=0)
pL.append(p)
pL[0].posV[0] = 10 ### ERROR, modifies pL[1].posV[0] as well
pL[0].mass = 42
print(pL[0].posV[0])
print(pL[1].posV[0]) ### Unexpected to be = 10, must be same memory
print(pL[2].posV[0]) ### Unexpected to be = 10, must be same memory
print(pL[0].mass)
print(pL[1].mass)
print(pL[2].mass)
if __name__ == "__main__":
main()
当我运行它时:
$ python test.py
10.0
10.0
10.0
42
0
0
似乎当我创建一个新的PARTICLE对象时,它看起来像每个新粒子的默认posV指向相同的内存块,因为如果我改变pL[0].posV[0]
,它也会改变pL[1].posV[1]
。然而,对于默认为标量的参数(例如质量),更改pL[0].mass
不会传播到pL[1].mass
。
:
- 请解释为什么修改
pL[0].posV[0]
也会改变pL[1].posV[0]
。这是怎么回事?
我怀疑它与指针和深vs浅拷贝有关,但我不确定到底发生了什么。直观地说,我希望创建一个新的PARTICLE实例应该创建一个全新的内存实例,每个新的PARTICLE对象都独立于之前的对象。显然不是这样的。
似乎当我创建一个新的粒子对象时,它看起来像每个新粒子的默认posV指向相同的内存块,因为如果我改变
pL[0].posV[0]
,它也会改变pL[1].posV[1]
在Python中,默认参数对求值一次在定义函数时,而不是每次调用函数时。这意味着在您的示例中,pL[0].posV
,pL[1].posV
等都指向您所说的相同对象(由np.zeros(3)
返回的numpy数组)。因此,一个引用中的变化也会反映在其他引用中。
但是对于默认为标量的参数(例如Mass),更改pL[0]。mass不传播到pL[1].mass.
不同之处在于numpy数组是可变对象,通过
pL[0].posV[0] = 10
你正在变异(更新第一个元素)pL[0].posV
指向的numpy数组,而
pL[0].mass = 42
是一个完全不同的操作。它创建一个全新的对象(整数42),并将其赋值给pL[0].mass
。pL[0].mass
现在指向一个不同的对象。注意,整数是不可变的,所以你不能以任何方式改变对象,并在对该对象的所有引用中反映这种改变。
我强烈推荐你阅读这些优秀的博客文章:
-
关于Python名称和值的事实和误解
-
Python漫游指南-常见问题¶