web-dev-qa-db-ja.com

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

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

<__main__.Test object at 0x2aba1c0cf890> 

__repr__()をオーバーロードし、super(Class, obj).__repr__()を呼び出して再使用する場合、メモリアドレスを保持する方法はありますか?

148
thr

Pythonマニュアル にはid()についての説明があります:

オブジェクトの「アイデンティティ」を返します。これは、そのオブジェクトの存続期間中に一意かつ一定であることが保証されている整数(または長整数)です。 (実装メモ:これはオブジェクトのアドレスです。)

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

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

183
Nick Johnson

この方法でデフォルトのreprを再実装できます。

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

使うだけ

id(object)
46
Ben Hoffstein

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

まず、 id は以下を返します:

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


CPythonでは、これはたまたまインタープリター内のオブジェクトを表す PyObject へのポインターであり、これはobject.__repr__と同じものです。ただし、これはCPythonの実装の詳細に過ぎず、一般的なPythonに当てはまるものではありません。 Jythonはポインターを処理せず、Java参照を処理します(もちろんJVMはおそらくポインターとして表されますが、それらは表示されません。GCは許可されているため、表示しません)それらを移動します)。 PyPyでは、さまざまな型にさまざまな種類のidを持たせることができますが、最も一般的なのは、idを呼び出したオブジェクトのテーブルへの単なるインデックスであり、これは明らかにポインターにはなりません。 IronPythonについてはわかりませんが、この点でCPythonよりもJythonに似ていると思われます。そのため、ほとんどの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 。ここでは関係ありません。これは、c_int32のようなctypesオブジェクト(および、おそらくnumpyが提供するような、いくつかのメモリバッファのようなオブジェクト)でのみ機能します。そして、そこであっても、c_int32値のアドレスを提供するのではなく、int32が終了するCレベルのc_int32のアドレスを提供します。

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

21
abarnert

Torstenへの応答で、通常のpythonオブジェクトでaddressof()を呼び出すことができませんでした。さらに、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
13
Peter Le Bek

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がサポートされています
  • メモリポインターは有効な概念です。

Edit:ctypesのインタープリター非依存性に関する情報を追加

4
Torsten Marek

あなたはその目的に適した何かを得ることができます:

id(self)
2
Thomas Wouters

id(object)がデフォルトのCPython実装でオブジェクトのアドレスを取得するのは事実ですが、これは一般的には役に立ちません... 行う 純粋なPythonコードからのアドレスを持つもの。

実際にアドレスを使用できるのは、C拡張ライブラリからのみです...この場合、Pythonオブジェクトは常にCポインターとして渡されるため、オブジェクトのアドレスを取得するのは簡単です。

0
Dan Lenski