Доступ к адресу памяти объекта

168

Когда вы звоните object.__repr__() метод в Python, вы получаете что-то вроде этого:

<__main__.Test object at 0x2aba1c0cf890> 

Есть ли способ получить адрес памяти при перегрузке __repr__(), кроме вызоваsuper(Class, obj).__repr__() и регулярного выражения?

Чет
источник

Ответы:

208

В руководстве по Python сказано следующее id():

Возвращает «идентификатор» объекта. Это целое число (или длинное целое число), которое гарантированно будет уникальным и постоянным для этого объекта в течение его времени жизни. Два объекта с непересекающимися временами жизни могут иметь одинаковое значение id (). (Примечание по реализации: это адрес объекта.)

Так что в CPython это будет адрес объекта. Однако нет никакой гарантии для любого другого интерпретатора Python.

Обратите внимание, что если вы пишете расширение C, у вас есть полный доступ к внутренним компонентам интерпретатора Python, включая прямой доступ к адресам объектов.

Ник Джонсон
источник
7
Это не универсальный ответ на вопрос; это относится только к CPython.
DilithiumMatrix
5
Примечание для себя: гарантия не распространяется на многопроцессорность
Rufus
1
Некоторые способы его использования (для сравнения значения, которое он содержит): forum.freecodecamp.com/t/python-id-object/19207
J. Делает
На что ссылается объект lifetime(и что это означает для жизни overlap/not overlap) в этом контексте?
Минь Чан
4
@MinhTran, поскольку id является адресом памяти объекта, он гарантированно уникален в процессе, и пока объект существует. Через некоторое время после сбора мусора память может быть использована повторно. Неперекрывающееся время жизни будет означать, что оригинальный объект больше не существует при создании нового объекта. Таким образом, это ограничение означает, что вы не можете безопасно использовать id () для создания хеша объекта для его хранения, освобождения и последующего восстановления.
Джошуа Клейтон
71

Вы можете переопределить repr по умолчанию следующим образом:

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )
Армин Ронахер
источник
1
Я знаю, что это старо, но вы можете просто делать return object.__repr__(self)или даже делать, object.__repr__(obj)когда вам это нужно, вместо того, чтобы создавать новый класс
Artyer
2
@Artyer: Как этот комментарий связан с оригинальным вопросом? Ответ, размещенный здесь, воссоздает адрес в соответствии с запросом исходного вопроса. Разве вы не должны натягивать mangle, если вы сделали это так, как вы предлагаете?
Rafe
1
Это кажется лучшим ответом для меня. Просто попробуйте сделать объект (), распечатать его, затем напечатать hex (id (object)), и результаты
Rafe
@Rafe Ваш ответ - многословный способ __repr__ = object.__repr__, и он не так уж и дурак, поскольку есть множество ситуаций, когда это не работает, например, переопределенная __getattribute__или не-CPython реализация, где идентификатор не является идентификатором. место в памяти. Это также не z-заполнение, поэтому вам придется работать, если система является 64-битной и добавлять нули по мере необходимости.
Artyer
@Artyer: мой пример показывает, как построить repr. Мы часто добавляем пользовательскую информацию (и я бы сказал, что это хорошая практика кодирования, поскольку она помогает в отладке). Мы интенсивно используем этот стиль, и я никогда не сталкивался с вашими крайними случаями. Спасибо, что поделились ими!
Rafe
52

Просто используйте

id(object)
Бен Хоффштейн
источник
6
который дает число. ... Что дальше? Могу ли я получить доступ к объекту с этим номером?
JLT
Вы можете проверить это id()@JLT
Билла Бегерадж
24

Здесь есть несколько вопросов, которые не охвачены другими ответами.

Во-первых, idтолько возвращается:

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


В CPython это указатель на on, который явно не будет указателем. Я не уверен насчет IronPython, но я подозреваю, что в этом отношении он больше похож на Jython, чем на CPython. Таким образом, в большинстве реализаций Python нет способа получить что-либо, показанное в этом , и бесполезно, если вы это сделали.PyObject на объект, представляющий объект в интерпретаторе, который object.__repr__отображает то же самое . Но это всего лишь деталь реализации CPython, а не то, что верно для Python в целом. Jython не имеет дело с указателями, он имеет дело с ссылками на Java (которые, конечно, JVM, вероятно, представляет в виде указателей, но вы не можете видеть их - и не захотите, потому что GC разрешено перемещать их). PyPy позволяет разным типам иметь разные виды id, но самый общий - это просто индекс в таблицу объектов, которые вы назвалиidrepr


Но что, если вы заботитесь только о CPython? В конце концов, это довольно распространенный случай.

Ну, во-первых, вы можете заметить, что idэто целое число; * если вы хотите эту 0x2aba1c0cf890строку вместо числа 46978822895760, вам придется форматировать ее самостоятельно. Под одеялом, я считаю object.__repr__, в конечном счете , используя printf«s %pформат, который вы не имеете с Python ... но вы всегда можете сделать это:

format(id(spam), '#010x' if sys.maxsize.bit_length() <= 32 else '#18x')

* В 3.x это int. В 2.x это intдостаточно большой размер, чтобы удерживать указатель, что может быть не так из-за проблем со знаком на некоторых платформах, и longдругое.

Можно ли что-нибудь сделать с этими указателями, кроме как распечатать их? Конечно (опять же, если вы заботитесь только о CPython).

Все функции C API получают указатель на PyObjectили связанный тип. Для этих связанных типов, вы можете просто вызвать, PyFoo_Checkчтобы убедиться, что это действительно Fooобъект, а затем привести с помощью (PyFoo *)p. Итак, если вы пишете расширение C, то idэто именно то, что вам нужно.

Что если вы пишете чистый код на Python? Вы можете вызвать ту же функцию , с pythonapiиз ctypes.


Наконец, несколько других ответов были подняты ctypes.addressof. Это не актуально здесь. Это работает только для ctypesтаких объектов, как c_int32(и, может быть, несколько объектов, подобных буферу памяти, как те, которые предоставляются numpy). И даже там, он не дает вам адрес c_int32значения, он дает вам адрес уровня C, int32который c_int32завершает.

Это, как говорится, чаще всего, если вы действительно думаете, что вам нужен адрес чего-то, вы изначально не хотели нативный объект Python, вы хотели ctypesобъект.

abarnert
источник
ну, это единственный способ хранить изменяемые объекты в картах / наборах, когда важна идентичность ...
Enerccio
@Enerccio Другое использование - в том idчисле использование их для хранения изменяемых значений в seenнаборе или cachedict - никак не зависит от того, idявляется ли указатель или каким-либо образом связано с repr. Именно поэтому такой код работает во всех реализациях Python, а не только в CPython.
abarnert
да, я использовал idдля этого, но я имею в виду, что даже в java вы можете получить адрес объекта, кажется странным, что в (C) Python нет пути, так как у него действительно стабильный gc, который не будет перемещать объекты, поэтому адрес остается прежним
Enerccio
@Enerccio Но вы не хотите использовать адрес объекта для кешируемого значения - вы хотите использовать объект для поиска id, независимо от того, является ли он адресом или нет. Например, в PyPy idон по-прежнему так же полезен, как и ключ в CPython, хотя обычно это просто указатель на какую-то скрытую таблицу в реализации, но указатель будет бесполезен, поскольку (как и в Java) объект может быть перемещен в объем памяти.
abarnert
@Enerccio Во всяком случае, там есть способ получить указатель в CPython. Как объяснено в ответе, CPython явно документирует, в качестве детали реализации, что idобъект является указателем на местоположение объекта в памяти. Итак, если у вас есть какое-либо использование для значения указателя (что вы почти никогда не делаете, как также объясняется в ответе) в специфичном для CPython коде, есть способ получить его, который задокументирован и гарантированно будет работать.
abarnert
13

Просто в ответ на Торстена я не смог вызвать addressof()обычный объект python. Кроме того, id(a) != addressof(a). Это в CPython, больше ничего не знаю.

>>> from ctypes import c_int, addressof
>>> a = 69
>>> addressof(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: invalid type
>>> b = c_int(69)
>>> addressof(b)
4300673472
>>> id(b)
4300673392
Питер Ле Бек
источник
4

С помощью ctypes вы можете добиться того же

>>> import ctypes
>>> a = (1,2,3)
>>> ctypes.addressof(a)
3077760748L

Документация:

addressof(C instance) -> integer
Вернуть адрес внутреннего буфера экземпляра C

Обратите внимание, что в CPython, в настоящее время id(a) == ctypes.addressof(a), ноctypes.addressof должен возвращать реальный адрес для каждой реализации Python, если

  • ctypes поддерживается
  • указатели памяти являются верным понятием.

Редактировать : добавлена ​​информация о независимости интерпретатора ctypes

Торстен Марек
источник
13
>>> импорт ctypes >>> a = (1,2,3) >>> ctypes.addressof (a) Traceback (последний вызов был последним): файл "<input>", строка 1, в <module> TypeError: неверный тип >>> id (a) 4493268872 >>>
5
Я согласен с Барри: приведенный выше код приводит к тому, TypeError: invalid typeчто я пробую его с Python 3.4.
Брэндон Родс
2

Вы можете получить что-то подходящее для этой цели с:

id(self)
Томас Воутерс
источник
1

Я знаю, что это старый вопрос, но если вы все еще программируете, в Python 3 в эти дни ... Я действительно обнаружил, что если это строка, то есть действительно простой способ сделать это:

>>> spam.upper
<built-in method upper of str object at 0x1042e4830>
>>> spam.upper()
'YO I NEED HELP!'
>>> id(spam)
4365109296

Преобразование строк также не влияет на расположение в памяти:

>>> spam = {437 : 'passphrase'}
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
>>> str(spam)
"{437: 'passphrase'}"
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
commanderbasher
источник
0

Хотя это правда, что id(object)получает адрес объекта в реализации CPython по умолчанию, это, как правило, бесполезно ... вы не можете ничего сделать с адресом из чистого кода Python.

Единственный раз, когда вы действительно сможете использовать адрес, это из библиотеки расширений C ... в этом случае получить адрес объекта тривиально, поскольку объекты Python всегда передаются как указатели C.

Дэн Ленски
источник
1
Если вы не используете встроенный ctypesинструментарий в стандартной библиотеке. В этом случае вы можете делать все что угодно с адресом :)
Брэндон Родс