Хранение элементов одного списка, в другом списке – по ссылке – в 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 имеет ссылку на список a , значение было обновлено – но где бы ни было b инициализировано (ссылка на?) Элементом из списка a , кажется, что Python расширил значение во время инициализации и, таким образом, хранит элемент по значению (не по ссылке), поэтому его значение явно не обновляется.

В принципе, я нашел вариант использования, где было бы удобно определить, например, b = [-1 a[2] -1] , а затем обновить a[2] и учесть, что последнее значение a[2] будет излучаться при получении значения (в данном случае) b[1] . Есть ли способ сделать это в Python, без необходимости делать b = [-1 a -1] , а затем читать b[1][2] (я хотел бы получить значение a[2] только по используя b[1] )?

a – ссылка на изменяемый список. Итак, когда вы говорите:

 a[2] = 55 

вы вызываете __setitem__ в списке, который устанавливает элемент в списке . list.__setitem__ не предпринимает попыток изменить элемент, который ранее был сохранен во втором индексе. Он просто заменяет эту ссылку новой.

С другой стороны, x = a[2] вызывает __getitem__ который просто создает новую ссылку на объект, хранящийся в этом индексе в списке.

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

Теперь есть 3 ссылки на объект 3000 в памяти ( 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] 

Чтобы решить проблему доступа к подспискам по индексу внешнего списка, вы можете использовать что-то вроде:

 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) 

Обратите внимание, что это, вероятно, сломается, если вы попытаетесь использовать срезы.