オブジェクトメモリアドレスへのアクセス


168

object.__repr__()Pythonでメソッドを呼び出すと、次のような結果が返されます。

<__main__.Test object at 0x2aba1c0cf890> 

あなたがオーバーロード場合、メモリアドレスのホールドを取得する方法はあります__repr__()、その後呼び出し、他の、super(Class, obj).__repr__()およびそれをregexing?

回答:


208

Pythonのマニュアルでは、これはについて言いたいことがありますid()

オブジェクトの「アイデンティティ」を返します。これは整数(または長整数)であり、このオブジェクトの存続期間中は一意で一定であることが保証されます。存続期間が重複しない2つのオブジェクトは、同じid()値を持つ場合があります。 (実装上の注意:これはオブジェクトのアドレスです。)

したがって、CPythonでは、これはオブジェクトのアドレスになります。ただし、他のPythonインタープリターではそのような保証はありません。

C拡張機能を作成している場合は、オブジェクトのアドレスへの直接アクセスを含む、Pythonインタープリターの内部への完全なアクセス権があることに注意してください。


7
これは質問に対する普遍的な答えではありません。CPythonにのみ適用されます。
DilithiumMatrix 2014年

5
自己への注意:保証はマルチプロセッシングには適用されません
ルーファス

1
それを使用するいくつかの方法(含まれている値を比較するため):forum.freecodecamp.com/t/python-id-object/19207
J. Does

このコンテキストでは、オブジェクトは何を意味しますlifetimeか(そしてそれが存続期間にとって何を意味するのoverlap/not overlapでしょうか)?
Minh Tran、

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:このコメントは元の質問とどう関係しているのですか?ここに投稿された答えは、元の質問で要求されたアドレスを再作成することです。もしあなたがあなたが提案した方法でそれをしたなら、マングルをひもでつなぐ必要はないでしょうか?
Rafe

1
これは私にとって最良の答えのようです。object()を作成して印刷し、hex(id(object))を印刷して、結果が一致するだけです
Rafe

@Rafeあなたの答えは長い時間をかけた方法で__repr__ = object.__repr__あり、これが機能しないさまざまな状況が存在するため、それほど簡単で__getattribute__はありません。メモリの場所。また、Zフィルも行わないため、システムが64ビットの場合は、必要に応じてゼロを追加する必要があります。
Artyer 2017年

@Artyer:私の例は、reprを作成する方法を示しています。多くの場合、カスタム情報を追加します(デバッグに役立つため、これは優れたコーディング手法だと思います)。私たちはこのスタイルを多用しており、私はあなたのエッジケースに出くわしたことはありません。それらを共有してくれてありがとう!
Rafe、


24

ここには、他の回答ではカバーされないいくつかの問題があります。

まず、以下idのみを返します:

オブジェクトの「アイデンティティ」。これは整数(または長整数)であり、その存続期間中、このオブジェクトに対して一意で一定であることが保証されています。重複しないライフタイムを持つ2つのオブジェクトは、同じid()値を持つ場合があります。


CPythonでは、これはたまたまPyObjectインタープリター内のオブジェクトを表すへのポインターです。これはオンになっているのと同じことですが、明らかにポインターにはなりません。IronPythonについてはわかりませんが、この点では、CPythonよりもJythonに近いと思います。したがって、ほとんどのPython実装では、何が表示されてもそれを取得する方法はありません。object.__repr__表示される。ただし、これはCPythonの実装の詳細にすぎず、Python全般に当てはまるものではありません。Jythonはポインターを処理せず、Java参照を処理します(もちろん、JVMはおそらくポインターとして表現しますが、GCがポインターを移動できるため、それらを表示することはできません。PyPyを使用すると、さまざまなタイプにさまざまな種類のを持たせることができますidが、最も一般的なのは、呼び出したオブジェクトのテーブルへのインデックスですidrepr


しかし、CPythonだけに関心がある場合はどうでしょうか。結局のところ、それはかなり一般的なケースです。

さて、最初に、それがid整数であることに気付くかもしれません。* 0x2aba1c0cf890数値の代わりにその文字列が必要な場合は、46978822895760自分でフォーマットする必要があります。裏では、object.__repr__最終的にはPythonにはないprintf%p形式を使用していると思いますが、いつでもこれを行うことができます。

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コードを作成している場合はどうなりますか?pythonapifromを使用して、まったく同じ関数を呼び出すことができますctypes


最後に、他のいくつかの答えが浮上しましたctypes.addressof。ここでは関係ありません。これは、などのctypesオブジェクトc_int32(およびによって提供されるような、いくつかのメモリバッファのようなオブジェクト)でのみ機能しますnumpy。そして、そこにさえ、それはあなたにc_int32値のアドレスを与えていません、それはあなたint32c_int32ラップするCレベルのアドレスを与えています。

そうは言っても、たいていの場合、何かのアドレスが本当に必要だと思うのであれば、そもそもネイティブPythonオブジェクトが必要ではなく、ctypesオブジェクトが必要でした。


よくこれは...アイデンティティが重要な場合にマップ/セットに可変オブジェクトを格納するための唯一の方法である
Enerccio

@Enerccioその他の用途— idそれらを使用してseenセットまたはcachedict 内の変更可能な値を保持することを含む— idは、ポインタであることに依存したり、何らかの方法でに関連付けたりしないでくださいrepr。そのため、このようなコードはCPythonでのみ機能するのではなく、すべてのPython実装で機能します。
abarnert 2018

ええ、私idはそれを使用しましたが、Javaでもオブジェクトのアドレスを取得できることを意味します、それは(C)Pythonに方法がないのは奇妙に見えますそれはオブジェクトを移動しない実際に安定したgcを持っているため、アドレスは同じままです
Enerccio

@Enerccioただし、キャッシュ可能な値にオブジェクトのアドレスを使用するid必要はありません。アドレスであるかどうかに関係なく、オブジェクトのfo を使用します。たとえば、PyPyではid、実装内の非表示のテーブルへのインデックスにすぎない場合でも、CPythonのキーと同じくらい便利ですが、(Javaのように)オブジェクトを移動できるため、ポインターは役に立ちません。メモリ。
abarnert

とにかく@Enerccio、そこはCPythonでのポインタを取得する方法。回答で説明したように、CPythonは実装固有の詳細としてid、オブジェクトのがメモリ内のオブジェクトの場所へのポインターであることを明示的に文書化しています。したがって、CPython固有のコードでポインター値を使用すると(回答で説明されているように、ほとんど使用しません)、文書化されて動作することが保証されている方法を使用できます。
abarnert

13

Torstenに応えて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)トレースバック(最後の最新呼び出し):ファイル "<input>"、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標準ライブラリの組み込みツールキットを使用しない限り。その場合、アドレスを使用してあらゆる種類のことを実行できます:)
Brandon Rhodes
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.