2013-05-22 50 views
6

我只是覺得我會記下這個,現在我已經看到了它 - 這將是很高興得到這種行爲的確認;我確實看到了How do I pass a variable by reference?,但我不確定在這種情況下如何解釋它。在Python的另一個列表中 - 通過引用存儲一個列表的元素?

比方說,我們有這兩個數組/列表:

a = [1, 2, 3, 4] 
b = [-1, a, -100, a[2], -1] 

解釋器最初將其視爲:

>>> print(a) 
[1, 2, 3, 4] 
>>> print(b) 
[-1, [1, 2, 3, 4], -100, 3, -1] 

現在,讓我們改變a[2],看看會發生什麼:

>>> print(a) 
[1, 2, 55, 4] 
>>> print(b) 
[-1, [1, 2, 55, 4], -100, 3, -1] 

因此,無論列表b有沒有引用lista,value has been updated - but b initialized with(from?)element from list a,Python似乎在初始化時擴展了這個值,因此按值存儲了元素(而不是參考),所以它的價值顯然不會更新。

基本上,我找到了一個用例,它可以很方便地定義例如b = [-1 a[2] -1],然後更新a[2],並且能夠在得到(在這種情況下)b[1]的值時計算出a[2]的最新值。有沒有辦法在Python中做到這一點,而不必做b = [-1 a -1],然後閱讀b[1][2](我想通過使用b[1]得到a[2]的值)?

回答

2

a是對可變列表的引用。所以,當你說:

a[2] = 55 

您正在調用哪一個列表設置的項目名單上__setitem__list.__setitem__不會嘗試改變曾經存儲在第二個索引中的項目。它只是用一個新的參考替換參考。

另一方面,x = a[2]調用__getitem__,它只是創建一個新對存儲在列表中該索引的對象的引用。

1
>>> a = [1000,2000,3000,4000] 
>>> sys.getrefcount(a[2]) 
2 
>>> b = [-1, a, -100, a[2], -1] 
>>> a is b[1] # b[1] and `a` are actually two variables pointing to the same object 
True 
#[1000,2000,3000,4000] can be accessed or modified by either `a` or `b[1]` 
>>> sys.getrefcount(a) 
3 

>>> sys.getrefcount(a[2]) 
3 

現在有對象3000內存總容量3個引用(a[2]b[-2]和外殼本身),但整數是不可變的,所以如果你改變的修改a[2]它會簡單地從對象中刪除一個參考3000,但b[-2]仍將指向內存中的同一對象,並且a[2]現在將指向某個新分配的對象。

>>> id(a[2]),id(b[-2]) 
(150561920, 150561920) 
>>> a[-2] = 5 
>>> id(a[2]),id(b[-2]) #b still points to the same object 
(148751024, 150561920) 
>>> sys.getrefcount(b[-2]) 
2 

如果a[2]該項目是一個可變對象,說list

>>> a = [1000,2000, [2] , 4000] 
>>> b = [-1, a, -100, a[2], -1] 
>>> a[2] += [5]  # we can modify [2] from from either a[2] or b[-2] 
>>> b[-2]+= [10] # `+=` , `list.extend`, `list.append` changes the list in-place 
>>> a[2] is b[-2] #both still points to the same object as lists are mutable 
True 
>>> a 
[1000, 2000, [2, 5, 10], 4000] 
>>> b 
[-1, [1000, 2000, [2, 5, 10], 4000], -100, [2, 5, 10], -1] 
0

爲了解決由外部列表的索引訪問子列表的問題,你可以使用類似:

class RecursiveList(list): 
    def getitem(self, index, recurse=True): 
     if not recurse: 
      return self[index] 
     else: 
      return list(RecursiveList.flatten(self))[index] 

    @staticmethod 
    def flatten(l): 
     for item in l: 
      if hasattr(item, "__iter__"): 
       for v in RecursiveList.flatten(item): 
        yield v 
      else: 
       yield item 

對於您要求的確切行爲,請添加以下內容:

def __getitem__(self, i): 
     return self.getitem(i, true) 

請注意,如果嘗試使用切片,這可能會中斷。

相關問題