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

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

<__main__.Test object at 0x2aba1c0cf890> 

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
179
0
202 366
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

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

id(object)

что дает номер. ... Что дальше? Могу ли я получить доступ к объекту с этим номером?

JLT 05.12.2016 19:25

Вы можете проверить это id() @JLT

Billal Begueradj 05.04.2017 14:35

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

id(self)
Ответ принят как подходящий

Руководство по Python говорит о id() следующее:

Return the "identity'' of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value. (Implementation note: this is the address of the object.)

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

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

Это нет универсальный ответ на вопрос; это применимо только к CPython.

DilithiumMatrix 09.10.2014 20:41

Примечание для себя: гарантия не распространяется на многопроцессорность.

Rufus 09.02.2017 06:33

Некоторые способы его использования (для сравнения содержащегося в нем значения): forum.freecodecamp.com/t/python-id-object/19207

J. Does 16.03.2017 00:33

На что в этом контексте ссылается lifetime объекта (и что это значит для времени жизни для overlap/not overlap)?

Minh Tran 31.05.2018 17:42

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

Joshua Clayton 02.08.2018 22:44

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

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )

Я знаю, что это старый, но вы можете просто сделать return object.__repr__(self) или даже просто object.__repr__(obj), когда вам это нужно, вместо создания нового класса

Artyer 02.11.2016 22:56

@Artyer: Какое отношение имеет этот комментарий к исходному вопросу? Ответ, размещенный здесь, воссоздает адрес в соответствии с исходным вопросом. Разве вам не пришлось бы натягивать нить, если бы вы сделали так, как вы предлагаете?

Rafe 13.03.2017 23:03

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

Rafe 13.03.2017 23:05

@Rafe Ваш ответ - это длинный способ сделать __repr__ = object.__repr__, и он далеко не так надежен, поскольку существует множество ситуаций, когда это не работает, например переопределенный __getattribute__ или реализация, отличная от CPython, где идентификатор не является ячейкой памяти. Он также не поддерживает z-заполнение, поэтому вам придется выяснить, является ли система 64-битной, и при необходимости добавить нули.

Artyer 15.03.2017 00:57

@Artyer: Мой пример показывает, как построить repr. Мы часто добавляем пользовательскую информацию (и я бы сказал, что это хорошая практика кодирования, поскольку она помогает при отладке). Мы активно используем этот стиль, и я никогда не сталкивался с вашими крайними случаями. Спасибо, что поделились ими!

Rafe 18.07.2017 19:08

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

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

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

addressof(C instance) -> integer
Return the address of the C instance internal buffer

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

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

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

>>> import ctypes >>> a = (1,2,3) >>> ctypes.addressof (a) Traceback (последний вызов последним): файл «<input>», строка 1, в <module> TypeError: неверный тип >>> id (a) 4493268872 >>>

user246672 28.08.2011 16:28

Я согласен с Барри: приведенный выше код приводит к TypeError: invalid type, когда я пробую его с Python 3.4.

Brandon Rhodes 24.06.2014 23:22

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

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

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

Brandon Rhodes 24.06.2014 23:22

Просто в ответ на Торстена я не смог вызвать 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

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

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

the “identity” of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.


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


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

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

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

* In 3.x, it's an int. In 2.x, it's an int if that's big enough to hold a pointer—which is may not be because of signed number issues on some platforms—and a long otherwise.

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

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

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


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

При этом, чаще всего, если вы действительно думаете, что вам нужен адрес чего-то, вам в первую очередь не нужен собственный объект Python, вам нужен объект ctypes.

ну, это единственный способ хранить изменяемые объекты в картах / наборах, когда важна идентичность ...

Enerccio 10.08.2018 19:08

@Enerccio Другие варианты использования id, в том числе их использование для хранения изменяемых значений в наборе seen или в диктовке cache, никоим образом не зависят от того, является ли id указателем или каким-либо образом связан с repr. Именно поэтому такой код работает во всех реализациях Python, а не только в CPython.

abarnert 10.08.2018 20:46

да, я использовал для этого id, но я имею в виду, что даже в java вы можете получить адрес объекта, кажется странным, что в (C) Python нет способа, поскольку у этого есть действительно стабильный gc, который не перемещает объекты, поэтому адрес остается такой же

Enerccio 10.08.2018 22:41

@Enerccio Но вы не хотите использовать адрес объекта для кэшируемого значения - вы хотите использовать id для объекта, независимо от того, является ли он адресом или нет. Например, в PyPy id по-прежнему так же полезен, как ключ в CPython, хотя обычно это просто индекс в какой-то скрытой таблице в реализации, но указатель был бы бесполезен, потому что (как и в Java) объект можно перемещать в памяти.

abarnert 11.08.2018 00:00

@Enerccio В любом случае, есть является способ получить указатель в CPython. Как объясняется в ответе, CPython явно документирует, как деталь, зависящую от реализации, что id объекта является указателем на расположение объекта в памяти. Итак, если у вас есть какое-либо использование значения указателя (чего вы почти никогда не делаете, как также объясняется в ответе) в коде, специфичном для CPython, есть способ получить его, документированный и гарантированно работающий.

abarnert 11.08.2018 00:03

Я знаю, что это старый вопрос, но если вы все еще программируете на 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>'

Другие вопросы по теме