python 是否在赋值时复制对象?
Does python copy objects on assignment?
我读到 python 中的赋值并不像在 c 中那样复制它,在 c 中它分配一个指向对象的指针。
但是当我调试这个函数时:
def popall(self):
objs = self.curstack
self.curstack = []
return objs
似乎正在进行某种复制。这个函数运行后obis是满的,self.curstack
是空的...
所以正在进行一些复制。是深还是浅?
它不复制任何东西。只是分配给 self.curstack
不会修改 self.curstack
所指的任何内容。它只是让 self.curstack
指向别的东西。
这样想。 self.curstack
指向一些东西。使用 objs = self.curstack
可以使 objs
指向相同的内容。使用 self.curstack = []
,您使 self.curstack
指向一个空列表。这不会删除 self.curstack
过去指向的任何内容;那东西还在,objs
还在指着它。
This article 使用标签标记的一个很好的类比来解释它。
Python 在任何地方都使用引用。所以你的代码是这样工作的:
objs = self.curstack
现在 objs
指向 curstack
的任何内容。
self.curstack = []
现在 curstack
指向一个空列表。 objs
不变,指向旧的curstack
。
return objs
你returnobjs
,也就是老curstack
.
让我们演示一下到底发生了什么(使用显示内存位置的 id
)
a = [1,2,3]
print id(a)
b = a
print id(b)
a = 4
print id(a)
print a
print b
4339130240
4339130240
4298163184
4
[1, 2, 3]
所以 a
和 b
最初指向内存中的同一个地方,但随后 a
指向新的地方。但是现在,让我们试试
a = [1,2,3]
print id(a)
b = a
print id(b)
a[0] = 4
print id(a)
print a
print b
4339098040
4339098040
4339098040
[4, 2, 3]
[4, 2, 3]
所以 b
不是 a
的全新副本。它与 a
是同一个对象(当它被定义时)。如果你然后做 a=4
,你在告诉 python,让我们忘记 a
用来指向什么并赋予它新的含义。 4 没有出现在内存中 a
用来引用 的地方。相反 a
现在指的是内存中的一个新位置。 b
仍然指代 a
最初指代的内容。
如果我们不重新分配 a
,而是更改 a
指向的对象中的其中一项,则检查 b
表明发生了相应的更改至 b
.
正如所有答案中提到的那样,self.curstack
不会修改它,只是指代某些东西。在你的例子中,它指的是一个空列表 []
。
您实际上可以使用 swampy
查看对象的引用方式
from swampy.Lumpy import Lumpy
class ref_test:
def __init__(self, curstack):
self.curstack = curstack
def popall(self):
lampy = Lumpy()
objs = self.curstack
self.curstack = []
print id(self.curstack), id(objs)
lampy.object_diagram()
return objs
o = ref_test([1,2,3,4])
res = o.popall()
print res
如您所见,self.curstack
指向一个空列表,而 objs
仍然保留 self.curstack
的旧值。
您可以使用id函数来检查对象的“身份”。对于 self.curstack
和 objs
,这在我们的案例中并不是唯一的
164711692 163838732
在Python中,有两个space,名称space和对象space。
名称只是您分配给对象的标签,这些标签存在于对象 space 中。对象有值和类型;名称只是为了方便我们;这样我们就可以访问对象 space.
中的内容
这与 C 等其他语言不同,在 C 语言中,变量更像是一个盒子,您可以在其中放置某种类型的东西。
在 Python 中,名称可以指向任何类型的任何对象 - 它们只是别名。
当您为对象指定名称时,如下所示:
a = 1
a
只是一个标签,指向具有类型 (int) 和值的对象 1
。如果您接下来这样做:
b = 1
现在您有两个名称 a
和 b
指向同一个对象 - 它们不是 "copies",而是指向同一个对象的两个标签。
当你这样做时:
a = 'hello'
现在 a
指向一个新对象,但是 b
仍然指向旧对象 (1
)。
最终,当没有名称指向对象时,Python 会进行自动垃圾收集以保持对象 space 优化。
我读到 python 中的赋值并不像在 c 中那样复制它,在 c 中它分配一个指向对象的指针。
但是当我调试这个函数时:
def popall(self):
objs = self.curstack
self.curstack = []
return objs
似乎正在进行某种复制。这个函数运行后obis是满的,self.curstack
是空的...
所以正在进行一些复制。是深还是浅?
它不复制任何东西。只是分配给 self.curstack
不会修改 self.curstack
所指的任何内容。它只是让 self.curstack
指向别的东西。
这样想。 self.curstack
指向一些东西。使用 objs = self.curstack
可以使 objs
指向相同的内容。使用 self.curstack = []
,您使 self.curstack
指向一个空列表。这不会删除 self.curstack
过去指向的任何内容;那东西还在,objs
还在指着它。
This article 使用标签标记的一个很好的类比来解释它。
Python 在任何地方都使用引用。所以你的代码是这样工作的:
objs = self.curstack
现在 objs
指向 curstack
的任何内容。
self.curstack = []
现在 curstack
指向一个空列表。 objs
不变,指向旧的curstack
。
return objs
你returnobjs
,也就是老curstack
.
让我们演示一下到底发生了什么(使用显示内存位置的 id
)
a = [1,2,3]
print id(a)
b = a
print id(b)
a = 4
print id(a)
print a
print b
4339130240
4339130240
4298163184
4
[1, 2, 3]
所以 a
和 b
最初指向内存中的同一个地方,但随后 a
指向新的地方。但是现在,让我们试试
a = [1,2,3]
print id(a)
b = a
print id(b)
a[0] = 4
print id(a)
print a
print b
4339098040
4339098040
4339098040
[4, 2, 3]
[4, 2, 3]
所以 b
不是 a
的全新副本。它与 a
是同一个对象(当它被定义时)。如果你然后做 a=4
,你在告诉 python,让我们忘记 a
用来指向什么并赋予它新的含义。 4 没有出现在内存中 a
用来引用 的地方。相反 a
现在指的是内存中的一个新位置。 b
仍然指代 a
最初指代的内容。
如果我们不重新分配 a
,而是更改 a
指向的对象中的其中一项,则检查 b
表明发生了相应的更改至 b
.
正如所有答案中提到的那样,self.curstack
不会修改它,只是指代某些东西。在你的例子中,它指的是一个空列表 []
。
您实际上可以使用 swampy
查看对象的引用方式 from swampy.Lumpy import Lumpy
class ref_test:
def __init__(self, curstack):
self.curstack = curstack
def popall(self):
lampy = Lumpy()
objs = self.curstack
self.curstack = []
print id(self.curstack), id(objs)
lampy.object_diagram()
return objs
o = ref_test([1,2,3,4])
res = o.popall()
print res
如您所见,self.curstack
指向一个空列表,而 objs
仍然保留 self.curstack
的旧值。
您可以使用id函数来检查对象的“身份”。对于 self.curstack
和 objs
164711692 163838732
在Python中,有两个space,名称space和对象space。
名称只是您分配给对象的标签,这些标签存在于对象 space 中。对象有值和类型;名称只是为了方便我们;这样我们就可以访问对象 space.
中的内容这与 C 等其他语言不同,在 C 语言中,变量更像是一个盒子,您可以在其中放置某种类型的东西。
在 Python 中,名称可以指向任何类型的任何对象 - 它们只是别名。
当您为对象指定名称时,如下所示:
a = 1
a
只是一个标签,指向具有类型 (int) 和值的对象 1
。如果您接下来这样做:
b = 1
现在您有两个名称 a
和 b
指向同一个对象 - 它们不是 "copies",而是指向同一个对象的两个标签。
当你这样做时:
a = 'hello'
现在 a
指向一个新对象,但是 b
仍然指向旧对象 (1
)。
最终,当没有名称指向对象时,Python 会进行自动垃圾收集以保持对象 space 优化。