元组中列表的Python变异

2024-06-06 15:21:33 发布

您现在位置:Python中文网/ 问答频道 /正文

在学习了列表、方框图和指针图之后,我决定为自己创建一些随机的东西,并测试一下我的知识。我将使用“浅拷贝”和“疑似浅拷贝”这两个词,因为我不太确定它们的定义是否正确。我的疑问是在为这些代码的行为提供原因时,请告诉我我是否思考正确。在

代码A

from copy import *

x=[1,[2,[3,[4]]]] #normal copy/hardcopy
a=x
v=list(x) #suspected shallow copy
y=x.copy() #shallow copy
z=deepcopy(x) #theoretical deep copy
w=x[:] #suspected shallow copy

def test():
    print("Original:",x)
    print("hardcopy:",a)
    print("suspected shallow copy",v)
    print("shallow copy",y)
    print("deep copy:",z)
    print("suspected shallow copy",w)

x[1]=x[1]+[4]
test()

输出A:

^{pr2}$

代码B

^{3}$

输出B: 我在这里看到了IDLE中的TypeError,但是list元素的变异仍然完成了,并且跨越了a、b、c

从输出B继续:

a[2][0]=a[2][0]+99
a,b,c

输出C:

((1, 2, [100, 2, 3, 3]), (1, 2, [100, 2, 3, 3]), (1, 2, [100, 2, 3, 3]))

代码D:

a=[1,2,(1,2,3)]

def shallow_copy(x):
    tup=[]
    for i in x:
        tup+=[i]
    return tup


def hardcopy(x):
    return x

b=hardcopy(a)
c=shallow_copy(a)
d=a.copy()
a[2]=a[2]+(4,)
a,b,c,d

输出D:

[1, 2, (1, 2, 3, 4)], [1, 2, (1, 2, 3, 4)], 
[1, 2, (1, 2, 3)], [1, 2, (1, 2, 3)]

From Output A, we observe the following: 1)For lists which have shallow copies, doing x[1]=x[1]+[4] does not affect the shallow copies. My reasons for the above could be

a) = followed by + does __add__ instead of __iadd__(which is +=), and doing __add__ should not modify the object, only changing the value for one pointer(x and its hardcopy in this case)

这在输出B中得到了进一步的支持,但在输出C中却有点矛盾,这可能部分是由于下面的原因(B),但不能太肯定。在

b) We executed this in the first layer(only 1 slice operator), maybe there's some kind of rule which prevents these elements from being modified.

输出B和输出C都支持这一点,尽管输出B可能被认为位于第一层,但可以将其视为增加第二层中的元素,它符合上述观察结果。在

2)What is the reason why the TypeError appeared in Output B, but is still executed? I know that whether an Exception might be triggered is based on the final sequence you are actually changing(the list in this case), but why is there still TypeError: 'tuple' object does not support item assignment ?

我对上述问题提出了自己的看法。我很欣赏关于这个问题的任何想法(最好是理论上的解决方案),因为我对编程还是比较陌生的。在


Tags: the代码inwhichforisdeflist
1条回答
网友
1楼 · 发布于 2024-06-06 15:21:33

回答问题1,这个问题看起来很复杂,但答案可能很简单:

当您有另一个名称引用原始对象时,您将看到原始对象中的更改。如果(!)您可以使用x[1] = x[1] + [4]的形式更改对象。这是因为您将一个新对象分配给x[1],而不是像x[1].append(4)中那样进行就地更改。在

您可以使用id()函数检查它。在

回答您的问题2,并改编自官方文件:

让我们做

a = (['hello'],)

那么

^{pr2}$

这和

a[0] = operator.iadd(a[0],[' world'])

iadd改变了列表的位置,但是分配失败了,因为您不能分配给元组(不可变类型)索引。在

如果你

a[0] = a[0] + [' world']

连接进入一个新的list对象,然后对元组索引的赋值也会失败。但是新的物体丢失了。a[0]未进行适当更改。在

为了澄清OP的评论,直接从here中的文档中可以看出

Many operations have an “in-place” version. Listed below are functions providing a more primitive access to in-place operators than the usual syntax does; for example, the statement x += y is equivalent to x = operator.iadd(x, y). Another way to put it is to say that z = operator.iadd(x, y) is equivalent to the compound statement z = x; z += y.

In those examples, note that when an in-place method is called, the computation and assignment are performed in two separate steps. The in-place functions listed below only do the first step, calling the in-place method. The second step, assignment, is not handled.

至于你的输出D: 写作

b = hardcopy(a)

除了写作什么都不做

b = a

实际上,b是一个新名称,它引用了a引用的同一对象。 这是因为a是可变的,因此指向原始对象的引用被传递到本地函数名x。返回x只是将相同的引用返回到b。在

这就是为什么您看到a中进一步的变化反映在b中。再次通过赋值使a[2]成为一个新的不同的对象元组,所以现在a[2]和{}引用一个新的元组(1,2,3,4),而{}和{}仍然引用旧的tuple对象。现在因为它们是元组,所以你不能像列表一样在适当的地方改变它们。在

至于“硬拷贝”这个词,我不会用的。它在官方文档中甚至没有出现过一次,在Python中提到的这个问题旁边的问题也会出现在其他上下文中。它是模棱两可的(与“浅”和“深”相反,这两个词很好地说明了它们的含义)。我认为与你描述的术语“硬拷贝”完全相反(一个对象副本)(指向同一对象的附加名称/引用/指针)。当然,最终有很多方法可以表达同样的观点。我们说“copy”是因为它比较短,而对于不可变的,复制是否发生并不重要(无论如何,您不能更改它们)。对于可变表来说,“copy”通常意味着“shallow copy”,因为如果您想要“deep copy”,您必须在代码中“更进一步”。在

相关问题 更多 >