Ссылки на основные данные Python, список тех же ссылок - PullRequest
7 голосов
/ 29 декабря 2010

Скажем, у меня есть два списка:

>>> 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, но не полностью.

Мои вопросы:

  1. Является ли изменяемый объект всегда ссылкой?Можете ли вы всегда предполагать, что его изменение модифицирует оригинал (если вы специально не делаете копию с l2=l1[:] идиомой)?

  2. Могу ли я собрать список всех одинаковых ссылок в Python?то есть какая-то функция f(l1), которая возвращает ['l1', 'd', 't'], если все они ссылаются на один и тот же список?

  3. Я предполагаю, что, несмотря ни на что, данные останутся действительными, поэтомуПока есть какая-то ссылка на это.

т.е.:

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

Ответы [ 6 ]

6 голосов
/ 29 декабря 2010

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) Если по действительному значению вы имеете в виду «не будет собирать мусор», то это правильно, за исключениеммаловероятная ошибкаЭто был бы очень жалкий сборщик мусора, который «украл» ваши данные.

3 голосов
/ 30 декабря 2010

Каждая переменная в Python является ссылкой.

Для списков вы сосредотачиваетесь на результатах метода append() и упускаете из виду общую картину структур данных Python.Есть и другие методы в списках, и есть свои преимущества и последствия для построения списка.Полезно рассматривать список как вид на другие объекты, указанные в списке.Они не «содержат» ничего, кроме правил и способов доступа к данным, на которые ссылаются объекты внутри них.

Метод list.append(x) , в частности , эквивалентен 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

Поместив конструктор списка вокруг l2 в l1[len(l1):]=[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 Первая репликация последовательности работает как положено;он создает 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]]

Вы также можете использовать понимание вложенного списка сделать то же самое так:

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

Для списков и ссылок у Фредрика Лунда есть этот текст для хорошего вступления.

Что касается ваших конкретных вопросов:

1) В Python Все является меткой или ссылкой на объект.Не существует «оригинала» (концепция C ++) и нет различия между «ссылкой», указателем или фактическими данными (концепция C / Perl)

2) У Фредрика Лунда есть отличная аналогия в отношении ссылок на вопрос, похожий на этот :

Точно так же, как вы получаете имя того кота, которого нашли на крыльце: сам кот (объект) не может сказать вам свое имяи на самом деле это не волнует - так что единственный способ узнать, как он называется, это спросить всех ваших соседей (пространства имен), является ли их кошка (объект) ...

.... ине удивляйтесь, если вы обнаружите, что он известен под многими именами, или вообще без имен!

Вы можете найти этот список с некоторыми усилиями, но почему?Просто назовите это так, как вы назовете - как найденный кот.

3) Верно.

0 голосов
/ 30 декабря 2010
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.

Да, объект не будет собирать мусор, если у вас есть ссылка на него.(Использование слова «действительный» здесь кажется неправильным, но я предполагаю, что вы это имели в виду).

0 голосов
/ 29 декабря 2010
  1. "... объект является ссылкой ..." - это нонсенс. Ссылки не являются объектами. Переменные, поля членов, слоты в списках и наборах и т. Д. Содержат ссылки, и эти ссылки указывают на объекты. Может быть любое число (в реализациях без рефактуации, даже ни одно - временно, то есть, пока GC не активируется) ссылки на объект. Каждый, кто имеет ссылку на объект, может вызывать его методы, получать доступ к его элементам и т. Д. - это верно для всех объектов. Конечно, только изменяемые объекты могут быть изменены таким образом, так что обычно вам нет дела до неизменных.
  2. Да, как показали другие. Но в этом нет необходимости, если только вы не отлаживаете сборщик мусора или не обнаруживаете серьезную утечку памяти в своем коде - почему вы думаете, что вам это нужно?
  3. Python имеет автоматическое управление памятью, так что да. Пока есть ссылка на объект, он не будет удален (однако он может оставаться в живых некоторое время после того, как он стал недоступным из-за циклических ссылок и того факта, что GC запускаются только время от времени).
0 голосов
/ 29 декабря 2010

Является ли изменяемый объект всегда ссылкой? Можете ли вы всегда предполагать, что его изменение изменяет оригинал (если вы специально не делаете копию с идиомой 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' ( Я не проверял).

Я предполагаю, что, несмотря ни на что, данные останутся действительными до тех пор, пока есть какая-то ссылка на них.

Абсолютно.

0 голосов
/ 29 декабря 2010

1- изменчивый объект всегда ссылка? Вы всегда можете предположить, что модифицируя это изменяет оригинал (Если вы специально не сделаете копию с l2 = l1 [:] идиома)?

Да. На самом деле неизменяемые объекты также всегда являются ссылками. Вы просто не можете изменить их, чтобы воспринимать это.

2 - Могу ли я собрать список всех те же ссылки в Python? т.е. некоторые функция f (l1), которая возвращает ['l1', 'd', 't'], если это все ссылаясь на тот же список?

Это странно, но это можно сделать.

Вы можете сравнивать объекты для "samenes" с оператором is. Как в l1 is t[0]

И вы можете получить все упомянутые объекты с помощью функции gc.get_referrers в модуле сборщика мусора (gc) - Вы можете проверить, какой из этих источников ссылается на ваш объект, с помощью оператора is. Так что да, это можно сделать. Я просто не думаю, что это была бы хорошая идея. Скорее предложение оператора is способ сделать то, что тебе нужно в одиночку

3- Я предполагаю, что независимо от того, что, данные останутся действительными, так Пока есть какая-то ссылка на него.

Да.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...