Python基本数据引用,列表相同的引用

时间:2010-12-29 19:36:35

标签: python data-structures reference

说我有两个清单:

>>> l1=[1,2,3,4]
>>> l2=[11,12,13,14]

我可以将这些列表放在元组或字典中,看起来它们都是回到原始列表的引用:

>>> t=(l1,l2)
>>> d={'l1':l1, 'l2':l2}
>>> id(l1)==id(d['l1'])==id(t[0])
True
>>> l1 is d['l1'] is t[0]
True

由于它们是引用,我可以更改l1,并且元组和字典中的引用数据会相应地更改:

>>> l1.append(5)
>>> l1
[1, 2, 3, 4, 5]
>>> t
([1, 2, 3, 4, 5], [11, 12, 13, 14])
>>> d
{'l2': [11, 12, 13, 14], 'l1': [1, 2, 3, 4, 5]}

包括我是否在字典d中附加引用或在元组t中附加可变引用:

>>> d['l1'].append(6)
>>> t[0].append(7)
>>> d
{'l2': [11, 12, 13, 14], 'l1': [1, 2, 3, 4, 5, 6, 7]}
>>> l1
[1, 2, 3, 4, 5, 6, 7]

如果我现在将l1设置为新列表,原始列表的引用计数会减少:

>>> sys.getrefcount(l1)
4
>>> sys.getrefcount(t[0])
4
>>> l1=['new','list']
>>> l1 is d['l1'] is t[0]
False
>>> sys.getrefcount(l1)
2
>>> sys.getrefcount(t[0])
3

附加或更改l1不会更改d['l1']t[0],因为它现在是一个新参考。间接引用的概念在Python文档中是covered fairly well但不完全。

我的问题:

  1. 可变对象总是是引用吗?你可以总是假设修改它会修改原文(除非你专门用l2=l1[:]成语制作副本)?

  2. 我可以在Python中汇编所有相同引用的列表吗?即某些函数f(l1)如果所有那些都引用同一个列表,则会返回['l1', 'd', 't']

  3. 我的假设是,无论如何,只要有一些参考,数据就会保持有效。

  4. 即:

    l=[1,2,3]         # create an object of three integers and create a ref to it
    l2=l              # create a reference to the same object
    l=[4,5,6]         # create a new object of 3 ints; the original now referenced 
                      # by l2 is unchanged and unmoved
    

6 个答案:

答案 0 :(得分:6)

1)通过引用修改可变对象将始终修改“原始”。老实说,这背叛了对参考文献的误解。较新的引用与任何其他引用一样多。只要两个名称都指向同一个对象,通过其他名称访问时,将反映通过任一名称修改对象。

2)不完全像你想要的那样。 gc.get_referrers返回对象的所有引用。

>>> l = [1, 2]
>>> d = {0: l}
>>> t = (l, )
>>> import gc
>>> import pprint
>>> pprint.pprint(gc.get_referrers(l))
[{'__builtins__': <module '__builtin__' (built-in)>,
  '__doc__': None,
  '__name__': '__main__',
  '__package__': None,
  'd': {0: [1, 2]},
  'gc': <module 'gc' (built-in)>,
  'l': [1, 2],
  'pprint': <module 'pprint' from '/usr/lib/python2.6/pprint.pyc'>,
  't': ([1, 2],)},   # This is globals()

 {0: [1, 2]},  # This is d
 ([1, 2],)]   # this is t

请注意,l引用的实际对象未包含在返回的列表中,因为它不包含对自身的引用。返回globals(),因为 包含对原始列表的引用。

3)如果有效,则表示“不会被垃圾收集”,那么这是正确的,除非是一个非常不可能的错误。这将是一个非常抱歉的垃圾收集器“窃取”你的数据。

答案 1 :(得分:3)

Python中的每个变量都是一个引用。

对于列表,您关注的是append()方法的结果,而忽略了Python数据结构的大局。列表上还有其他方法,列表的构建方式也有其优点和结果。将列表视为对列表中引用的其他对象的视图是有帮助的。除了访问其中对象所引用数据的规则和方法之外,它们不“包含”任何其他内容。

list.append(x) method specifically相当于l[len(l):]=[list]

所以:

>>> l1=range(3)
>>> l2=range(20,23)
>>> l3=range(30,33)
>>> l1[len(l1):]=[l2]    # equivalent to 'append' for subscriptable sequences 
>>> l1[len(l1):]=l3      # same as 'extend'
>>> l1
[0, 1, 2, [20, 21, 22], 30, 31, 32]
>>> len(l1)
7
>>> l1.index(30)
4
>>> l1.index(20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.index(x): x not in list
>>> 20 in l1
False
>>> 30 in l1
True

通过将列表构造函数放在l1[len(l1):]=[l2]中的l2周围,或调用l.append(l2),可以创建绑定到l2的引用。如果更改l2,引用也将显示更改。列表中的长度是单个元素 - 对附加序列的引用。

如果没有l1[len(l1):]=l3中的构造函数快捷方式,则复制序列的每个元素。

如果使用其他常用列表方法,例如l.index(something)in,则无法在数据引用中找到元素。 l.sort()无法正确排序。它们是对象的“浅层”操作,通过使用l1[len(l1):]=[l2],您现在可以创建递归数据结构。

如果您使用l1[len(l1):]=l3,则会在l3中制作元素的真实(浅)副本。

这些是相当基本的Python习语,而且大多数时候他们都做对了。但是,您可以获得令人惊讶的结果,例如:

>>> m=[[None]*2]*3
>>> m
[[None, None], [None, None], [None, None]]
>>> m[0][1]=33
>>> m
[[None, 33], [None, 33], [None, 33]]   # probably not what was intended...
>>> m[0] is m[1] is m[2]               # same object, that's why they all changed
True

一些Python新手尝试通过执行类似m=[[None]*2]*3之类的操作来创建多维度。第一个sequence replication按预期工作;它创建了2份None。这是第二个问题:它创建了对第一个列表的引用的三个副本。因此,输入m[0][1]=33会修改绑定到m的列表中的列表,然后所有绑定的引用都会更改以显示更改。

比较:

>>> m=[[None]*2,[None]*2,[None]*2]
>>> m
[[None, None], [None, None], [None, None]]
>>> m[0][1]=33
>>> m
[[None, 33], [None, None], [None, None]]

你也可以使用nested list comprehensions来做同样的事情:

>>> m=[[ None for i  in range(2)] for j in range(3)]
>>> m
[[None, None], [None, None], [None, None]]
>>> m[0][1]=44
>>> m
[[None, 44], [None, None], [None, None]]
>>> m[0] is m[1] is m[2]                      # three different lists....
False

对于列表和参考文献,Fredrik Lundh有this text作为一个很好的介绍。

关于您的具体问题:

1)在Python中, Everything 是标签或对象的引用。没有'原创'(C ++概念),'引用',指针或实际数据(C / Perl概念)之间没有区别

2)Fredrik Lundh在参考to a question similar to this中有一个很好的类比:

  

就像你得到的名字一样   你在门廊上找到的那只猫:   猫(对象)本身不能告诉你   它的名字,并没有真正关心    - 所以找出它的名字的唯一方法就是问你所有的   邻居(名称空间),如果是他们的   猫(对象)......

     

....如果你愿意,也不要感到惊讶   发现它被许多名字所知,或者   根本没有名字!

您可以通过一些努力找到此列表,但为什么?只要称它为你将要称之为它 - 就像找到的猫一样。

3)是的。

答案 2 :(得分:0)

  

1-一个可变对象总是一个   参考?你能不能总是这么想   修改它会修改原件   (除非你特意制作副本   用l2 = l1 [:]种习语?

是。实际上,非可变对象也总是一个参考。 你无法改变它们来感知这一点。

  

2 - 我可以收集所有的清单   Python中的相同引用?即一些   函数f(l1)返回['l1',   'd','t']如果那些都是   提到同一个清单?

这很奇怪,但可以做到。

您可以将“samenes”的对象与is运算符进行比较。 就像l1 is t[0]

一样

您可以使用该功能获取所有引用的对象 垃圾收集器模块(gc)中的gc.get_referrers - 您可以使用is运算符检查这些引荐中的哪些引用您的对象。所以,是的,它可以做到。 我只是觉得这不是一个好主意。 is运营商更有可能提供此服务 一种让你独自完成的方式

  

3-我的假设是无论如何   什么,数据将保持有效   只要有一些参考。

是。

答案 3 :(得分:0)

  

可变对象总是引用吗?你能不能一直认为修改它会修改原文(除非你专门用l2 = l1 [:]那种习语制作一个副本)?

Python具有引用语义:变量不像C ++那样存储值,而是标记它们。 “原始”的概念是有缺陷的:如果两个变量标记相同的值,那么“先到先得”是完全无关紧要的。如果对象是否可变是无关紧要的(除了不可变对象不会让它很容易分辨出幕后发生的事情)。要以更通用的方式制作副本,请尝试copy模块。

  

我可以在Python中汇编所有相同引用的列表吗?即,某些函数f(l1)返回['l1','d','t'],如果所有这些都指的是同一个列表?

不容易。有关详细信息,请参阅aaronasterling的答案。您也可以尝试类似k for k, v in locals().items() if v is the_object的内容,但是您还必须搜索globals(),您会错过一些内容,并且由于名称'k'的递归而可能会导致某些问题'v'(我还没有测试过。)

  

我的假设是,无论如何,只要有一些参考资料,数据将保持有效。

绝对

答案 4 :(得分:0)

  1. “......对象是参考......”是无稽之谈。引用不是对象。变量,成员字段,列表和集合中的槽等保存引用,这些引用指向对象。可以有任何数字(在非refchouting实现中,甚至没有 - 暂时,即直到GC启动)对对象的引用。每个拥有对象引用的人都可以调用它的方法,访问它的成员等等 - 对于所有对象都是如此。当然,只有可变对象可以通过这种方式进行更改,因此您通常不关心不可变对象。
  2. 是的,正如其他人所表明的那样。但是,除非您正在调试GC或跟踪代码中严重的内存泄漏,否则这不应该是必要的 - 您认为为什么需要这样做?
  3. Python有自动内存管理,所以是的。只要存在对对象的引用,它就不会被删除(但是,由于循环引用以及GC只偶尔运行一次这一事实,它可能会在无法访问后保持一段时间)。 / LI>

答案 5 :(得分:0)

1a. Is a mutable object always a reference?

可变对象和非可变对象之间没有区别。将变量名称视为引用对于具有C背景的人有用(但暗示它们可以被解除引用,但它们不能被解除引用)。

1b. Can you always assume that modifying it modifies the original

请,它不是“原件”。这是同一个对象。 b = a表示b,a现在是同一个对象。

1c. (Unless you specifically make a copy with l2=l1[:] kind of idiom)?

是的,因为那时它不再是同一个对象了。 (尽管列表中的条目与原始列表的对象相同)。

2. Can I assemble a list of all the same references in Python?

是的,可能,但你永远不会需要它,所以这将浪费精力。 :)

3. It is my assumption that no matter what, the data will remain valid so long as there is some reference to it.

是的,只要您引用了对象,就不会对对象进行垃圾回收。 (在这里使用“有效”一词似乎不正确,但我认为这就是你的意思)。