Доступ к адресу памяти объекта - PullRequest
141 голосов
/ 23 сентября 2008

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

<__main__.Test object at 0x2aba1c0cf890> 

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

Ответы [ 8 ]

176 голосов
/ 23 сентября 2008

В руководстве Python есть что сказать о id():

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

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

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

63 голосов
/ 23 сентября 2008

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

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )
46 голосов
/ 23 сентября 2008

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

id(object)
21 голосов
/ 09 октября 2014

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

Сначала id возвращает только:

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


В 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')

* В 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.

13 голосов
/ 07 января 2011

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

С 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

2 голосов
/ 23 сентября 2008

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

id(self)
0 голосов
/ 23 сентября 2008

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

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

...