【问题标题】:Why does this assigned object share the same memory space as the original object?为什么这个分配的对象与原始对象共享相同的内存空间?
【发布时间】:2014-09-04 16:15:05
【问题描述】:

在 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 应该访问第二个项目,但会产生 [],因为它们共享相同的内存

我的问题是为什么会这样?不应该将组分配给 toup 意味着 toup 将在不同的十六进制地址位置拥有组内存的副本吗?

我还能做些什么来规避这个问题,这样我就不必编写两个 groupby 迭代了?

【问题讨论】:

  • 这取决于变量的类型。如果将整数和字符串等原语分配给另一个变量,则会复制它们,而实例对象则不会 - 变量将成为对实例对象的引用。试试 "a= []; b= a; print(a is b)" - 这将打印 True。
  • “变量赋值意味着为新变量分配它自己的内存而不是指向原始内存的指针” - 不正确,这不是 Python 名称的工作方式(参见例如 @987654321 @)。使用toup = group[:] 创建副本。
  • @Rawing 不正确(首先,Python 并没有真正的原语,例如整数 are 实例);不同之处在于,例如整数是不可变的,而例如列表是可变的。 a = 1; b = a; a is b仍然是True,只是b += 1不会影响a(因为整数是不可变的)。
  • @jonrsharpe:我明白了,感谢您的更正。

标签: python memory itertools


【解决方案1】:

你说:

在python中,变量赋值是指给新变量赋值 自己的内存而不是指向原始内存的指针(来自我的 理解如果这是不正确的,请告诉我):

这是不正确的。 Python 名称可以具有(有时)类似于 C 变量的方面,也可以具有(有时)类似于 C 指针的方面。试图说他们像一个或另一个只是令人困惑。不。将它们视为 Python 的独特和惯用的。

Python 的“变量”更应该被视为名称。多个可能引用同一个内存位置,即使您并不打算这样做。

例子:

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

并且(由于interning,以下可能为真。请参阅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

(一定要理解另一个classic mistake所有Python程序员迟早都会做的。这是由于对单个可变对象的多次引用然后期望一个引用像单个对象一样。)

正如 jonrsharpe 在 cmets 中指出的那样,阅读 Ned Batchelders 出色的 Facts and myths about Python Names and Values 或 How to Think Like a Pythonista 以获得更详细的概述。

【讨论】:

  • 你真的应该在第二个例子中提到实习 - 300 is 60*5 不会发生这种情况(或在所有 Python 实现中 - 这是一个 CPython 细节)。
  • “Python 名称可以具有(有时)类似于 C 变量的方面,也可以具有(有时)类似于 C 指针的方面”它们总是类似于 C 指针。没有按值“复制”变量的通用方法。
  • @PaulDraper:我可能不会低估你的评论,但a=[1,2,3] 和b=a 在 Python 中的抽象逻辑上相似,但在底层却截然不同。
  • @dawg,在 C 中,a = b 会将 b 的值复制到 a。在 Python 中没有办法做到这一点。您唯一可以“复制”的东西是指针。 (所有变量都是指针。)
  • 给您的问题:那么为什么分配给不同名称的数字和布尔值具有相同的内存地址(您的第一个示例),但是同样不可变的元组将分配给不同的地址,例如x = 1, y = 1, id(x) != id(y) (解析为真)。另外,当第一次启动解释器时,我注意到用列表尝试它实际上产生了相同的共享地址......这是一种取决于当前内存状态的优化吗?
【解决方案2】:

在python中,变量赋值是指为新变量分配自己的内存,而不是指向原始内存的指针

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

我的问题是为什么会这样?不应该将组分配给 toup 意味着 toup 将在不同的十六进制地址位置拥有组内存的副本吗?

两个变量都指向同一个对象。当你迭代一个并耗尽迭代器时,迭代第二个变量会给你一个空序列。

【讨论】:

    猜你喜欢
    • 2021-05-16
    • 1970-01-01
    • 2011-08-06
    • 2011-10-14
    • 2015-08-18
    • 1970-01-01
    • 2013-08-03
    • 2015-10-15
    • 2010-09-16
    相关资源
    最近更新 更多