Одним из наиболее запутанных аспектов в python является создание списка списков (при условии, что один не использует numpy) - например, если вы попытаетесь сделать это с помощью более простого умножения, вы получите ссылочные копии:
In [1]: a = [[0] * 4] * 4
In [2]: a
Out[2]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
In [3]: a[0][1] = 1
In [4]: a
Out[4]: [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]
Как упоминалось в различных других публикациях SO, таких как этот, правильный способ создания экземпляра без каких-либо ссылок будет следующим:
In [5]: b = [[0 for i in range(4)] for i in range(4)]
In [6]: b
Out[6]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
In [7]: b[0][1] = 1
In [8]: b
Out[8]: [[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Вопрос заключается в следующем: предположим, что каждый определяет свой список как со списком a
, есть ли способ проверить массив таким образом, чтобы он показал, что он использует ссылки? Простая печать массива не будет раскрывать ссылки.
Сначала немного терминологии: вы говорите о списках (а не массивах), которые всегда хранят ссылки на свои элементы.
Быстрый способ проверить, все ли ссылки в списке относятся к разным объектам:
>>> l1 = [[0, 1], [0, 1]]
>>> l2 = [[0, 1]]*2
>>>
>>> len(set(map(id, l1))) == len(l1) # no duplicates
True
>>> len(set(map(id, l2))) == len(l2) # duplicates
False
который просто проверяет, есть ли n
уникальных идентификаторов для объектов в списке длины n
.
Если в вашем списке очень большое количество элементов, было бы более эффективно сделать это лениво и вернуть False
для первого дублированного идентификатора.
def all_unique(lst):
seen = set()
for x in lst:
id_ = id(x)
if id_ in seen:
return False
seen.add(id_)
return True
... работает так:
>>> all_unique(l1)
True
>>> all_unique(l2)
False
Вы можете использовать функцию id
:
>>> a = [[0] * 4] * 4
>>> a
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> [id(sublist) for sublist in a]
[1975671202696, 1975671202696, 1975671202696, 1975671202696]
>>> b = [[0 for i in range(4)] for i in range(4)]
>>> b
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> [id(sublist) for sublist in b]
[1975671204808, 1975671205128, 1975671205000, 1975671204872]
Как вы можете видеть, в a
идентификаторы одинаковы, а в b
они разные.
sys.getrefcount()
который может указывать, сколько ссылок указывают на объект