为什么这个赋值对象与原对象共享同一内存空间?

0 投票
2 回答
2721 浏览
提问于 2025-04-19 20:40

在使用Python的itertools模块中的groupby时,我遇到了一个奇怪的现象。

在Python中,给变量赋值意味着给这个新变量分配一块自己的内存,而不是指向原来的内存(如果我理解错了,请告诉我):

y = 7
x = y    
y = 9

x will still be 7

但是当我使用groupby模块时,我是用这个模块把有相同键的项目分到一个组里。我想要两个组,因为重新遍历原来的组是没用的,因为内存已经被修改了。举个例子:

for key, group in groupby(rows, lambda x: x[0]):

    data = [thing[1] for thing in group] #accesses 1st attribute of element
    data2 = [thing[2] for thing in group] # would yield [] as group is empty

所以我试了这个:

for key, group in groupby(rows, lambda x: x[0]):
    #create a copy of group to reiterate over
    toup = group

    print toup #<itertools._grouper object at 0x1039a8850>
    print group #<itertools._grouper object at 0x1039a8850>

    data = [thing[1] for thing in group] #accesses 1st attribute of element
    data2 = [thing[2] for thing in toup]

data2应该能访问第二个项目,但却返回了[],因为它们共享了同一块内存。

我想问的是,为什么会这样?把group赋值给toup,难道不应该让toup在不同的内存地址上有一份group的副本吗?

还有,我该怎么做才能绕过这个问题,这样我就不需要写两次groupby的循环了?

2 个回答

0

在Python中,给变量赋值意味着给新变量分配自己的内存,而不是指向原来的内存。

Python有可变对象(比如列表、迭代器,几乎所有东西)和不可变对象(比如整数和字符串)。无论是哪种情况,赋值都不会复制对象。对于不可变对象,所有对它们的操作都会生成一个新的实例,所以你不会像处理可变类型那样遇到“修改”整数或字符串的问题。

我的问题是,为什么会这样?把group赋值给toup,不应该意味着toup会在不同的内存地址上有一个group的副本吗?

这两个变量都会指向同一个对象。当你遍历一个变量并用完迭代器后,再遍历第二个变量时,会得到一个空的序列。

3

你说:

在Python中,变量赋值意味着给新变量分配自己的内存,而不是指向原始内存(根据我的理解,如果我说错了请告诉我):

这个说法不对。Python中的名字有些时候像C语言的变量,有些时候又像C语言的指针。试图把它们归类为其中一种只会让人困惑。不要这样想。应该把它们看作是Python独特的特性。

Python中的“变量”更应该被理解为名字。多个名字可能指向同一个内存位置,即使你并不想这样。

举个例子:

>>> y=7
>>> x=7
>>> x is y
True
>>> id(x)
140316099265400
>>> id(y)
140316099265400

而且(由于字符串驻留,以下内容可能是正确的。有关短整型驻留的更多信息,请参见PEP 237,但这属于实现细节):

>>> x=9
>>> y=5+4
>>> x is y
True

Python中的is运算符通过比较内存地址来判断两个对象是否相同,如果相同则返回True。id函数返回这个地址。

再举个例子:

>>> li1=[1,2,3]
>>> li2=[1,2,3]
>>> li1==li2
True
>>> li1 is li2
False

即使li1 == li2,它们也必须是不同的列表,否则如果你改变其中一个,两个都会改变,如下例所示:

>>> li1=[1,2,3]
>>> li2=li1
>>> li1.append(4)
>>> li2
[1, 2, 3, 4]
>>> li1==li2
True
>>> li1 is li2
True

(一定要理解另一个经典错误,所有Python程序员迟早都会犯。这是因为多个名字引用了同一个可变对象,然后期望其中一个名字像单一对象那样工作。)

正如jonrsharpe在评论中指出的,建议阅读Ned Batchelder的优秀文章关于Python名字和值的事实与误区如何像Pythonista那样思考,以获取更详细的概述。

撰写回答