了解 Python 类 中的范围/复制,其中参数默认为 numpy 向量
Understanding Scope / Copy in Python Classes where arguments default to numpy vectors
我有一个非常简单的 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]
.但是对于默认为标量的参数(例如质量),changine pL[0].mass
不会传播到 pL[1].mass
.
问题 :
- 请解释为什么修改
pL[0].posV[0]
也会更改 pL[1].posV[0]
。这是怎么回事?
我怀疑它与指针和深拷贝与浅拷贝有关,但我不确定到底发生了什么。直觉上,我希望创建一个新的 PARTICLE 实例应该创建一个全新的内存实例,每个新的 PARTICLE 对象都独立于以前的对象。显然不是这样。
It seems that when I create a new PARTICLE object, it looks like the default posV for each new particle points to the same block of memory because if I change pL[0].posV[0]
it ALSO changes pL[1].posV[1]
在 Python 中,默认参数在定义函数时计算 一次 ,而不是每次调用函数时计算。这意味着在您的示例中 pL[0].posV
、pL[1].posV
等都指向您所说的同一个对象(np.zeros(3)
返回的 numpy 数组)。因此,其中一个的更改也反映在其他参考文献中。
However for args that default to scalars (e.g. Mass), changine pL[0].mass does NOT propagate to 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-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]
.但是对于默认为标量的参数(例如质量),changine pL[0].mass
不会传播到 pL[1].mass
.
问题 :
- 请解释为什么修改
pL[0].posV[0]
也会更改pL[1].posV[0]
。这是怎么回事?
我怀疑它与指针和深拷贝与浅拷贝有关,但我不确定到底发生了什么。直觉上,我希望创建一个新的 PARTICLE 实例应该创建一个全新的内存实例,每个新的 PARTICLE 对象都独立于以前的对象。显然不是这样。
It seems that when I create a new PARTICLE object, it looks like the default posV for each new particle points to the same block of memory because if I change
pL[0].posV[0]
it ALSO changespL[1].posV[1]
在 Python 中,默认参数在定义函数时计算 一次 ,而不是每次调用函数时计算。这意味着在您的示例中 pL[0].posV
、pL[1].posV
等都指向您所说的同一个对象(np.zeros(3)
返回的 numpy 数组)。因此,其中一个的更改也反映在其他参考文献中。
However for args that default to scalars (e.g. Mass), changine pL[0].mass does NOT propagate to pL[1].mass.
区别在于 numpy 数组是可变对象,通过做
pL[0].posV[0] = 10
你正在变异(更新第一个元素)pL[0].posV
指向的numpy数组,而
pL[0].mass = 42
是一个完全不同的操作。它 创建 一个全新的对象(整数 42)并将其分配回 pL[0].mass
。 pL[0].mass
现在指的是不同的对象。请注意,整数是不可变的,因此您不能以任何方式更改对象并在对该对象的所有引用中反映该更改。
我强烈建议您阅读这些优秀的博文: