14

在 David Beazley 的“Python Essential Reference”的第 35 页上,他首先指出:

对于字符串等不可变数据,解释器积极地在程序的不同部分之间共享对象。

然而,稍后在同一页上,他指出

对于数字和字符串等不可变对象,此分配有效地创建了一个副本。

但这不是矛盾吗?一方面他说它们是共享的,但他又说它们是复制的。

4

3 回答 3

10

python 中的赋值永远不会创建副本(从技术上讲,只有当类成员的赋值被重新定义时,例如通过使用__setattr__、属性或描述符重新定义时才有可能)。

所以之后

a = foo()
b = a

从那里返回的任何内容foo都没有被复制,而是您有两个变量ab指向同一个对象。无论对象是否不可变。

但是,对于不可变对象,很难判断是否是这种情况(因为您不能使用一个变量来改变对象并使用另一个变量检查​​更改是否可见),因此您可以自由地认为确实a并且b不能相互影响。

对于一些不可变对象,Python 也可以自由地重用旧对象而不是创建新对象和之后

a = x + y
b = x + y

其中xy都是数字(因此总和是数字并且是不可变的)可能两者都a指向b同一个对象。请注意,没有这样的保证......也可能是它们将指向具有相同值的不同对象。

要记住的重要一点是 Python 永远不会复制,除非特别指示使用 egcopydeepcopy. 这对于可变对象非常重要,以避免意外。

例如,您可以看到一个常见的成语:

class Polygon:
    def __init__(self, pts):
        self.pts = pts[:]
    ...

在这种情况下self.pts = pts[:],使用而不是self.pts = pts制作整个点数组的副本,以确保如果在创建对象后将更改应用于传递给构造函数的列表,点列表不会意外更改。

于 2012-07-20T19:57:54.330 回答
6

有效地创建了一个副本。它实际上并没有创建副本。拥有两个副本和拥有两个名称共享相同值的主要区别在于,在后一种情况下,通过一个名称进行的修改会影响另一个名称的值。如果值不能被改变,这种差异就消失了,所以对于不可变对象,值是否被复制几乎没有实际影响。

id在某些极端情况下,即使对于不可变类型(例如,通过使用函数或运算符),您也可以区分副本和不同对象之间的区别is,但这些对于 Python 内置的不可变类型(如字符串和数字)没有用。

于 2012-07-20T19:39:58.870 回答
0

不,将预先存在的str变量分配给新变量名称不会在内存中创建该值的独立副本。

可以使用该id()函数检查内存中唯一对象的存在。例如,使用交互式 Python 提示符,尝试:

>>> str1 = 'ATG'
>>> str2 = str1

两者str1str2具有相同的值:

>>> str1
'ATG'
>>> str2
'ATG'

这是因为str1str2都指向同一个对象,这可以从它们共享相同的唯一对象 ID 的事实中得到证明:

>>> id(str1)
140439014052080
>>> id(str2)
140439014052080
>>> id(str1) == id(str2)
True

现在假设您修改str1

>>> str1 += 'TAG'  # same as str1 = str1 + 'TAG'
>>> str1
ATGTAG

因为str对象是不可变的,所以上面的赋值创建了一个具有自己 ID 的新的唯一对象:

>>> id(str1)
140439016777456
>>> id(str1) == id(str2)
False

但是,str2保持与之前相同的 ID:

>>> id(str2)
140439014052080

因此,执行将具有自己唯一 ID 的str1 += 'TAG'全新对象分配给变量,同时继续指向原始对象。strstr1str2str

这意味着将现有str变量分配给另一个变量名称不会在内存中创建其值的副本。

于 2021-07-01T11:53:10.447 回答