Доступ к адресу памяти объекта
-
02-07-2019 - |
Вопрос
Когда вы звоните в object.__repr__()
метода в Python, вы получите что-то вроде этого:
<__main__.Test object at 0x2aba1c0cf890>
Есть ли способ получить адрес памяти, если вы перегружаете __repr__()
, кроме звонка super(Class, obj).__repr__()
и повторное выражение?
Решение
А Руководство по Python есть что сказать об этом id()
:
Возвращает «идентичность» объекта.Это целое число (или длинное целое число), которое гарантированно будет уникальным и постоянным для этого объекта в течение жизни.Два объекта с непересекающим временем жизни могут иметь одинаковое значение ID ().(Примечание по реализации:это адрес объекта.)
Итак, в CPython это будет адрес объекта.Однако такой гарантии нет для любого другого интерпретатора Python.
Обратите внимание: если вы пишете расширение C, у вас есть полный доступ ко внутреннему устройству интерпретатора Python, включая прямой доступ к адресам объектов.
Другие советы
Вы можете переопределить представление по умолчанию следующим образом:
def __repr__(self):
return '<%s.%s object at %s>' % (
self.__class__.__module__,
self.__class__.__name__,
hex(id(self))
)
Просто используйте
id(object)
Здесь есть несколько вопросов, которые не охвачены ни одним из других ответов.
Первый, id
возвращает только:
«идентичность» объекта.Это целое число (или длинное целое число), которое гарантированно будет уникальным и постоянным для этого объекта в течение его жизни.Два объекта с непересекающимися сроками жизни могут иметь одинаковое
id()
ценить.
В CPython это указатель на PyObject
который представляет объект в интерпретаторе, и это то же самое, что object.__repr__
дисплеи.Но это всего лишь деталь реализации CPython, а не то, что справедливо для Python в целом.Jython не имеет дело с указателями, он имеет дело с ссылками Java (которые JVM, конечно, вероятно, представляет как указатели, но вы не можете их видеть — и не хотели бы, потому что GC разрешено перемещать их).PyPy позволяет различным типам иметь разные виды id
, но наиболее общим является просто индекс таблицы объектов, которые вы вызвали id
on, который, очевидно, не будет указателем.Я не уверен насчет IronPython, но подозреваю, что в этом отношении он больше похож на Jython, чем на CPython.Таким образом, в большинстве реализаций Python невозможно получить то, что отображается в этом repr
, и бесполезно, если бы вы это сделали.
Но что, если вас интересует только 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
объект.
Просто в ответ Торстену я не смог позвонить 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
С cтипы, вы можете добиться того же самого с помощью
>>> 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 от интерпретатора
Вы можете получить что-то подходящее для этой цели с помощью:
id(self)
Хотя это правда, что id(object)
получает адрес объекта в реализации CPython по умолчанию, это вообще бесполезно...ты не можешь делать что-нибудь с адресом из чистого кода Python.
Единственный раз, когда вы действительно сможете использовать адрес, - это библиотека расширений C...в этом случае получить адрес объекта тривиально, поскольку объекты Python всегда передаются как указатели C.