说我有两个清单:
>>> 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但不完全。
我的问题:
可变对象总是是引用吗?你可以总是假设修改它会修改原文(除非你专门用l2=l1[:]
成语制作副本)?
我可以在Python中汇编所有相同引用的列表吗?即某些函数f(l1)
如果所有那些都引用同一个列表,则会返回['l1', 'd', 't']
?
我的假设是,无论如何,只要有一些参考,数据就会保持有效。
即:
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
答案 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)
答案 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.
是的,只要您引用了对象,就不会对对象进行垃圾回收。 (在这里使用“有效”一词似乎不正确,但我认为这就是你的意思)。