2013-05-22 36 views
6

Po prostu pomyślałem, że zanotuję to teraz, gdy już to widziałem - dobrze byłoby uzyskać potwierdzenie tego zachowania; Widziałem How do I pass a variable by reference?, ale nie jestem pewien, jak interpretować go w tym kontekście.Przechowywanie elementów z jednej listy, na innej liście - przez odniesienie - w języku Python?

Powiedzmy mamy te dwie tablice/list:

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

Interpreter początkowo widzi je jako:

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

Teraz zmieńmy a[2] i zobaczyć, co się dzieje:

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

Gdziekolwiek lista b ma odniesienie do listaa wartość została zaktualizowana - ale gdziekolwiek b została zainicjowana (odniesienie do?) Element od listy a, wydaje się, że Python rozszerzony wartość w czasie inicjalizacji, a zatem przechowywany element o wartość (nie przez odniesienie), więc jego wartość oczywiście nie aktualizuje się.

Zasadniczo znalazłem przypadek użycia, w którym wygodnie byłoby określić np. b = [-1 a[2] -1], a następnie zaktualizować a[2] i być w stanie policzyć, że najnowsza wartość a[2] zostanie wyemitowana podczas pobierania wartości (w tym przypadku) b[1]. Czy istnieje sposób, aby to zrobić w Pythonie, bez konieczności wykonywania b = [-1 a -1], a następnie czytanie b[1][2] (Chciałbym uzyskać wartość a[2] tylko za pomocą b[1])?

Odpowiedz

2

a jest odniesieniem do zmieniającej się listy. Tak więc, kiedy mówisz:

a[2] = 55 

dzwonisz __setitem__ na liście, która ustawia pozycję na liście. list.__setitem__ nie podejmuje żadnej próby zmutowania elementu, który był przechowywany w drugim indeksie. Po prostu zastępuje to odniesienie nowym.

Po drugiej stronie x = a[2] dzwoni __getitem__, która właśnie tworzy nowe odniesienie do obiektu przechowywanego w tym indeksie na liście.

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 

Obecnie istnieje całkowity 3 odniesienia do obiektu w pamięci (3000 a[2], b[-2] i Shell siebie), ale jako całkowite są niezmienne, więc jeśli zmiana modyfikować a[2] będzie to po prostu usunąć jedno odwołanie od obiektu 3000, ale b[-2] nadal będzie wskazywać na ten sam obiekt w pamięci, a a[2] wskaże teraz na nowo przydzielony obiekt.

>>> 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 

Jeśli pozycja na a[2] jest zmienny obiekt, powiedzmy 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

Aby rozwiązać problem dostępu do listy zagnieżdżone przez indeks listy zewnętrznej, można użyć coś takiego:

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 

Dokładne zachowanie, o które prosisz, należy dodać:

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

Uwaga, to prawdopodobnie pęknie, jeśli spróbujesz użyć plasterków.