Доступ до адреси об’єктної пам'яті


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
Примітка для себе: Гарантія не поширюється на багатообробні процеси
Руфус

1
Деякі способи його використання (порівняння значення, яке воно містить): forum.freecodecamp.com/t/python-id-object/19207
J. Чи

На що посилається об'єкт lifetime(і що це означає протягом життя overlap/not overlap) у цьому контексті?
Мінь Тран

4
@MinhTran, оскільки ідентифікатор є адресою пам'яті об'єкта, він гарантується унікальним у процесі, і поки об’єкт існує. Через деякий час після об'єкта зібраний сміття пам'ять може бути повторно використана. Термін експлуатації, що не перекривається, означав би, що оригінальний об'єкт більше не існує, коли створюється новий об'єкт. Отже, це обмеження означає, що ви не можете безпечно використовувати id () для створення хеш-об’єкта, щоб зберігати його, звільнити його та пізніше відновити.
Джошуа Клейтон

71

Ви можете повторно виконати повторне перевидання за замовчуванням таким чином:

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: Що стосується цього коментаря до оригінального питання? Відповідь, розміщена тут, відтворює адресу, як вимагає оригінальне запитання. Чи не доведеться вам нанизувати мангал, якби ви робили це так, як пропонуєте?
Рейф

1
Це здається мені найкращою відповіддю. Просто спробуйте створити об’єкт (), роздрукуйте його, потім надрукуйте шістнадцятковий (id (об’єкт)) і результати відповідають
Rafe

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

@Artyer: Мій приклад показує, як побудувати репр. Ми часто додаємо власну інформацію (і я б сказав, що це хороша практика кодування, оскільки вона допомагає налагоджувати). Ми дуже використовуємо цей стиль, і я ніколи не стикався з вашими краєвидами. Дякуємо, що поділилися ними!
Rafe


24

Тут є кілька питань, на які не висвітлено жодна з інших відповідей.

По-перше, 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).

Всі функції API 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об'єкт.


ну це єдиний спосіб зберігання змінних об’єктів у картах / наборах, коли важлива ідентичність ...
Enerccio

@Enerccio Інші способи - idвключаючи їх використання для зберігання змінних значень у seenмножині чи cacheдиктаті - не залежать від будь-якого способу від того, idщо є вказівником, або пов’язані будь-яким чином з repr. Саме тому такий код працює у всіх реалізаціях Python, а не лише у CPython.
abarnert

так, я використовував idдля цього, але я маю на увазі, що навіть у Java ви можете отримати адресу об'єкта, здається дивним, що немає ніякого шляху в (C) Python, оскільки у нього є фактично стабільний gc, який не переміщуватиме об'єкти, тому адреса залишається такою ж
Enerccio

@Enerccio Але ви не хочете використовувати адресу об'єкта для кешованого значення - ви хочете використовувати idfo об'єкт, будь то адреса чи ні. Наприклад, у 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

З типом ви можете домогтися того ж самого

>>> 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 підтримується
  • покажчики пам'яті - це дійсне поняття.

Редагувати : додана інформація про незалежність інтерпретаторів типів


13
>>> імпортувати типи >>> a = (1,2,3) >>> ctypes.addressof (a) Відстеження (останній останній дзвінок останній): Файл "<введення>", рядок 1, в <module> TypeError: недійсний тип >>> id (a) 4493268872 >>>

5
Я погоджуюся з Баррі: наведений вище код приводить, TypeError: invalid typeколи я пробую його з Python 3.4.
Брендон Родос


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

0

Хоча це правда, що id(object)отримує адресу об’єкта в реалізації за замовчуванням CPython, це, як правило, марно ... ви не можете нічого зробити з адресою з чистого коду Python.

Єдиний раз, коли ви насправді зможете скористатись адресою, це бібліотека розширень C ... в цьому випадку тривіально отримати адресу об’єкта, оскільки об’єкти Python завжди передаються навколо як покажчики C.


1
Якщо ви не використовуєте вбудований ctypesінструментарій у Стандартній бібліотеці. У такому випадку ви можете робити всілякі речі з адресою :)
Брендон Родос,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.