理解Python类中的作用域/复制,其中参数默认为numpy vector



我有一个非常简单的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

:

  1. 请解释为什么修改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].masspL[0].mass现在指向一个不同的对象。注意,整数是不可变的,所以你不能以任何方式改变对象,并在对该对象的所有引用中反映这种改变。

我强烈推荐你阅读这些优秀的博客文章:

  • 关于Python名称和值的事实和误解

  • Python漫游指南-常见问题¶

最新更新