1

在一节中,作者写了两句自相矛盾的句子。

第一个:

A shallow copy of an object is defined to be a newly created object of the same 
type as the original object whose contents are references to the elements in the 
original object. 

第二个:

when shallow copies are made, the string is explicitly copied and a new (string) 
object created 

第一句表示字符串对象和列表对象在浅复制时都是引用(未显式复制)。

第二句意味着字符串对象在浅拷贝时被显式拷贝。

我觉得第一句话是对的。我认为第二个应该是:

when "wifey[0] = 'jane'" is executed, the string is explicitly copied and a new 
(string) object created 

我不知道作者为什么要写第二个。我对吗?

这是整个材料:

6.20。*复制Python对象和浅拷贝和深拷贝

在前面的 3.5 节中,我们描述了对象赋值是简单的对象引用。这意味着当您创建一个对象,然后将该对象分配给另一个变量时,Python 不会复制该对象。相反,它只复制对对象的引用。

例如,假设您想为一对年轻夫妇创建一个通用配置文件;称其为人。然后你为他们两个复制这个对象。在下面的示例中,我们展示了两种复制对象的方法,一种使用切片,另一种使用工厂函数。为了显示我们有三个不相关的对象,我们使用 id() 内置函数向您显示每个对象的身份。(我们也可以使用 is 操作符来做同样的事情。)

>>> person = ['name', ['savings', 100.00]]
>>> hubby = person[:]       # slice copy
>>> wifey = list(person)    # fac func copy
>>> [id(x) for x in person, hubby, wifey]
[11826320, 12223552, 11850936]

为他们创建个人储蓄账户,初始存款为 100 美元。更改名称以自定义每个人的对象。但是,当丈夫提取 50.00 美元时,他的行为影响了他妻子的账户,尽管已经制作了单独的副本。(当然,这是假设我们希望他们拥有单独的账户,而不是单一的联名账户。)这是为什么呢?

>>> hubby[0] = 'joe'
>>> wifey[0] = 'jane'
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])

原因是我们只做了一个浅拷贝。对象的浅拷贝被定义为与原始对象具有相同类型的新创建的对象,其内容是对原始对象中元素的引用。换句话说,复制的对象本身是新的,但内容不是。序列对象的浅拷贝是默认的拷贝类型,可以通过多种方式制作:(1) 获取完整的切片 [:],(2) 使用工厂函数,例如 list()、dict()、等等,或者 (3) 使用 copy 模块的 copy() 函数。

你的下一个问题应该是:在分配妻子的名字时,为什么不影响丈夫的名字?他们现在不应该都有'jane'这个名字吗?它起作用并且我们没有重复名称的原因是因为它们的每个列表中有两个对象,第一个是不可变的(字符串),第二个是可变的(列表)。因此,当进行浅拷贝时,会显式复制字符串并创建一个新的(字符串)对象而该列表仅复制其参考,而不复制其成员。因此,更改名称不是问题,但更改其银行信息的任何部分都是问题。在这里,让我们看一下每个列表元素的对象 ID。请注意,banking 对象是完全相同的,并且更改一个对象的原因会影响另一个对象。请注意,在我们更改名称后,新的名称字符串如何替换原始的“名称”字符串:

前:

>>> [id(x) for x in hubby]
[9919616, 11826320]
>>> [id(x) for x in wifey]
[9919616, 11826320]

后:

>>> [id(x) for x in hubby]
[12092832, 11826320]
>>> [id(x) for x in wifey]
[12191712, 11826320]
4

1 回答 1

2

你的下一个问题应该是:在分配妻子的名字时,为什么不影响丈夫的名字?他们现在不应该都有'jane'这个名字吗?它起作用并且我们没有重复名称的原因是因为它们的每个列表中有两个对象,第一个是不可变的(字符串),第二个是可变的(列表)。

尽管引用的事实是真实的,但这种解释是不正确的。实际上,它起作用并且我们没有重复名称的原因是该行为wifey[0] = 'jane'的元素分配了一个新值,该元素wifeyhubby. 相比之下,该行hubby[1][1] = 50.0没有为 的元素分配新值hubby。相反,它为 的元素分配了一个新值,hubby[1]如前所述,该元素与 是相同的对象wifey[1]

hubby[0]并且wifey[0]恰好是不可变值的事实是真实的,但无关紧要。

你可以证明这一点:

person = [[], ['savings', 100.00]]
hubby = list(person)
wifey = list(person)

因此,这两个元素都是可变的,因为它们都是列表。

hubby[0] = [1,2,3]
id(hubby[0])
id(wifey[0])

看哪,现在第一个元素指的是不同的列表!

因此,当进行浅拷贝时,字符串被显式复制并创建一个新的(字符串)对象,而列表只复制了它的引用,而不是它的成员。

我想不出任何方法来解释这是真的。如果您对包含字符串的内容进行浅拷贝,则不会复制该字符串。列表的情况完全相同,如果您对包含列表的内容进行浅拷贝,则不会复制该列表。

如果您复制字符串本身,则实际上仍然不需要复制字符串(因为它是不可变的)。作为一种优化,您可以再次返回相同的对象,并且对于大多数目的而言,这同样好。事实上,Python 2.7.5 和 Python 3.2.5(这是我在这里安装的)都对所有三个original[:],str(original)copy.copy(original).

如果出于某种晦涩的原因,您实际上想要两个不相同的相等字符串(也许是为了测试性能==或其他东西,我不知道),那么您基本上必须尝试欺骗 Python 运行时:(original + ' ')[0:-1]或其他任何东西。

于 2013-10-28T09:08:59.223 回答