Mengakses Alamat Memori Objek

168

Ketika Anda memanggil object.__repr__()metode dengan Python Anda mendapatkan sesuatu seperti ini kembali:

<__main__.Test object at 0x2aba1c0cf890> 

Apakah ada cara untuk mendapatkan alamat memori jika Anda membebani __repr__(), selain itu memanggil super(Class, obj).__repr__()dan regex keluar?

thr
sumber

Jawaban:

208

The pengguna Python telah mengatakan tentang id():

Kembalikan "identitas" suatu objek. Ini adalah bilangan bulat (atau bilangan bulat panjang) yang dijamin unik dan konstan untuk objek ini selama masa pakainya. Dua objek dengan masa hidup yang tidak tumpang tindih mungkin memiliki nilai id () yang sama. (Catatan Implementasi: ini adalah alamat objek.)

Jadi dalam CPython, ini akan menjadi alamat objek. Namun, tidak ada jaminan untuk interpreter Python lainnya.

Perhatikan bahwa jika Anda menulis ekstensi C, Anda memiliki akses penuh ke internal interpreter Python, termasuk akses ke alamat objek secara langsung.

Nick Johnson
sumber
7
Ini bukan jawaban universal untuk pertanyaan; ini hanya berlaku untuk CPython.
DilithiumMatrix
5
Catatan untuk diri sendiri: Jaminan tidak berlaku untuk multi
Rufus
1
Beberapa cara untuk menggunakannya (untuk membandingkan nilai yang dikandungnya): forum.freecodecamp.com/t/python-id-object/19207
J.
Apa yang dimaksud objek lifetime(dan apa artinya seumur hidup overlap/not overlap) dalam konteks ini?
Minh Tran
4
@MinhTran karena id adalah alamat memori objek, dijamin unik dalam prosesnya, dan sementara objek tersebut ada. Beberapa saat setelah objek sampah dikumpulkan, memori dapat digunakan kembali. Seumur hidup yang tidak tumpang tindih akan berarti objek asli tidak ada lagi ketika objek baru dibuat. Jadi batasan ini berarti Anda tidak dapat menggunakan id () dengan aman untuk membuat hash objek untuk disimpan, membebaskannya, dan kemudian mengaktifkannya kembali.
Joshua Clayton
71

Anda dapat menerapkan ulang repr default dengan cara ini:

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )
Armin Ronacher
sumber
1
Saya tahu ini sudah tua, tetapi Anda bisa melakukan return object.__repr__(self)atau bahkan hanya melakukan object.__repr__(obj)kapan pun Anda membutuhkan ini alih-alih membuat kelas baru
Artyer
2
@Artyer: Apa hubungannya komentar ini dengan pertanyaan aslinya? Jawaban yang diposting di sini adalah menciptakan kembali alamat seperti yang diminta oleh pertanyaan asli. Tidakkah Anda harus merangkai tali jika Anda melakukannya seperti yang Anda sarankan?
Rafe
1
Ini sepertinya jawaban terbaik untukku. Coba saja buat objek (), cetak, lalu cetak hex (id (objek)) dan hasilnya cocok
Rafe
@Rafe Jawaban Anda adalah cara yang panjang lebar untuk melakukan __repr__ = object.__repr__, dan bukan sebagai bukti bodoh, karena ada berbagai situasi di mana ini tidak berhasil, misalnya ditimpa__getattribute__ implementasi yang ditimpa atau non-CPython di mana id tidak lokasi memori. Itu juga tidak z-fill, jadi Anda harus bekerja jika sistemnya 64bit dan menambahkan angka nol seperlunya.
Artyer
@ Artyer: Contoh saya menunjukkan cara membuat repr. Kami sering menambahkan informasi khusus (dan saya akan mengatakan ini adalah praktik pengkodean yang baik karena membantu dalam debugging). Kami menggunakan gaya ini dengan berat dan saya tidak pernah lari ke kasing tepi Anda. Terima kasih telah membagikannya!
Rafe
52

Gunakan saja

id(object)
Ben Hoffstein
sumber
6
yang memberi nomor. ... Apa berikutnya? Bisakah saya mengakses objek dengan nomor itu?
JLT
Anda dapat memeriksa id()@JLT ini
Billal Begueradj
24

Ada beberapa masalah di sini yang tidak dicakup oleh jawaban lain mana pun.

Pertama, idhanya mengembalikan:

"identitas" suatu objek. Ini adalah bilangan bulat (atau bilangan bulat panjang) yang dijamin unik dan konstan untuk objek ini selama masa pakainya. Dua objek dengan masa hidup yang tidak tumpang tindih mungkin memiliki nilai yang sama id().


Dalam CPython, ini adalah penunjuk ke PyObjectyang mewakili objek dalam penafsir, yang merupakan hal yang sama yang object.__repr__ditampilkan. Tapi ini hanyalah detail implementasi dari CPython, bukan sesuatu yang berlaku untuk Python secara umum. Jython tidak berurusan dengan pointer, ia berurusan dengan referensi Java (yang tentu saja mewakili JVM sebagai pointer, tetapi Anda tidak dapat melihat itu — dan tidak mau, karena GC diizinkan untuk memindahkannya). PyPy memungkinkan berbagai jenis memiliki jenis yang berbeda id, tetapi yang paling umum hanyalah indeks ke dalam tabel objek yang Anda panggilid pada, yang jelas tidak akan menjadi penunjuk. Saya tidak yakin tentang IronPython, tapi saya menduga ini lebih seperti Jython daripada seperti CPython dalam hal ini. Jadi, di sebagian besar implementasi Python, tidak ada cara untuk mendapatkan apa pun yang muncul dalam hal itu repr, dan tidak ada gunanya jika Anda melakukannya.


Tetapi bagaimana jika Anda hanya peduli tentang CPython? Bagaimanapun, itu adalah kasus yang cukup umum.

Nah, pertama, Anda mungkin melihat itu idadalah bilangan bulat; * jika Anda menginginkan 0x2aba1c0cf890string itu alih-alih nomornya 46978822895760, Anda harus memformatnya sendiri. Di bawah selimut, saya percaya object.__repr__akhirnya menggunakan printf's %pformat, yang Anda tidak memiliki dari Python ... tapi Anda selalu dapat melakukan hal ini:

format(id(spam), '#010x' if sys.maxsize.bit_length() <= 32 else '#18x')

* Dalam 3.x, ini adalah int. Dalam 2.x, itu adalah intjika itu cukup besar untuk memegang pointer — yang mungkin bukan karena masalah nomor yang ditandatangani pada beberapa platform — dan longsebaliknya.

Apakah ada yang bisa Anda lakukan dengan petunjuk ini selain mencetaknya? Tentu (sekali lagi, anggap Anda hanya peduli tentang CPython).

Semua fungsi C API mengambil pointer ke tipe PyObjectatau yang terkait. Untuk jenis terkait, Anda bisa menelepon PyFoo_Checkuntuk memastikan itu benar-benar Fooobjek, lalu dilemparkan dengan (PyFoo *)p. Jadi, jika Anda menulis ekstensi C, iditulah yang Anda butuhkan.

Bagaimana jika Anda sedang menulis kode Python murni? Anda dapat memanggil fungsi yang sama persis dengan pythonapidari ctypes.


Akhirnya, beberapa jawaban lain muncul ctypes.addressof. Itu tidak relevan di sini. Ini hanya berfungsi untuk ctypesobjek seperti c_int32(dan mungkin beberapa objek seperti buffer memori, seperti yang disediakan oleh numpy). Dan, bahkan di sana, itu tidak memberi Anda alamat c_int32nilai, itu memberi Anda alamat tingkat-C int32yang c_int32dirangkum.

Yang sedang berkata, lebih sering daripada tidak, jika Anda benar-benar berpikir Anda perlu alamat sesuatu, Anda tidak ingin objek Python asli di tempat pertama, Anda menginginkan ctypesobjek.

abarnert
sumber
nah ini satu-satunya cara untuk menyimpan objek yang bisa berubah di peta / set ketika identitas penting ...
Enerccio
@Enerccio Penggunaan lain id— termasuk menggunakannya untuk menyimpan nilai yang bisa berubah dalam satu seenset atau cachedict — tidak bergantung pada cara apa pun idmenjadi penunjuk, atau terkait dengan cara apa pun dengan repr. Itulah sebabnya kode tersebut bekerja di semua implementasi Python, bukan hanya bekerja di CPython.
abarnert
ya, saya menggunakan iduntuk itu, tapi maksud saya masih bahkan di java Anda bisa mendapatkan alamat objek, tampaknya aneh tidak ada cara (C) Python karena yang benar-benar stabil gc yang tidak akan memindahkan objek sehingga alamat tetap sama
Enerccio
@Enerccio Tetapi Anda tidak ingin menggunakan alamat suatu objek untuk nilai yang dapat disimpan - Anda ingin menggunakan idobjek itu, apakah itu alamat atau bukan. Misalnya, dalam PyPy, idmasih sama bermanfaatnya dengan kunci pada CPython, meskipun biasanya hanya indeks ke beberapa tabel tersembunyi dalam implementasi, tetapi sebuah pointer tidak akan berguna, karena (seperti Java) objek dapat dipindahkan dalam Penyimpanan.
abarnert
@Enerccio Pokoknya, ada adalah cara untuk mendapatkan pointer di CPython. Seperti dijelaskan dalam jawabannya, CPython secara eksplisit mendokumentasikan, sebagai detail implementasi khusus, bahwa idobjek adalah pointer ke lokasi objek dalam memori. Jadi, jika Anda menggunakan nilai pointer (yang hampir tidak pernah Anda lakukan, seperti juga dijelaskan dalam jawaban) dalam kode khusus CPython, ada cara untuk mendapatkannya yang didokumentasikan dan dijamin berfungsi.
abarnert
13

Hanya untuk menanggapi Torsten, saya tidak dapat memanggil addressof()objek python biasa. Selanjutnya id(a) != addressof(a),. Ini di CPython, tidak tahu tentang hal lain.

>>> 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
Peter Le Bek
sumber
4

Dengan ctypes , Anda dapat mencapai hal yang sama dengannya

>>> import ctypes
>>> a = (1,2,3)
>>> ctypes.addressof(a)
3077760748L

Dokumentasi:

addressof(C instance) -> integer
Kembalikan alamat buffer internal instance C

Perhatikan bahwa dalam CPython, saat ini id(a) == ctypes.addressof(a), tetapi ctypes.addressofharus mengembalikan alamat asli untuk setiap implementasi Python, jika

  • ctypes didukung
  • pointer memori adalah gagasan yang valid.

Sunting : menambahkan informasi tentang independensi interpreter dari ctypes

Torsten Marek
sumber
13
>>> impor ctypes >>> a = (1,2,3) >>> ctypes.addressof (a) Traceback (panggilan terakhir terakhir): File "<input>", baris 1, dalam <module> TypeError: jenis tidak valid >>> id (a) 4493268872 >>>
5
Saya setuju dengan Barry: kode di atas menghasilkan TypeError: invalid typeketika saya mencobanya dengan Python 3.4.
Brandon Rhodes
2

Anda bisa mendapatkan sesuatu yang cocok untuk tujuan itu dengan:

id(self)
Thomas Wouters
sumber
1

Saya tahu ini adalah pertanyaan lama tetapi jika Anda masih memprogram, dalam python 3 hari ini ... Saya benar-benar menemukan bahwa jika itu adalah string, maka ada cara yang sangat mudah untuk melakukan ini:

>>> spam.upper
<built-in method upper of str object at 0x1042e4830>
>>> spam.upper()
'YO I NEED HELP!'
>>> id(spam)
4365109296

konversi string tidak mempengaruhi lokasi dalam memori juga:

>>> spam = {437 : 'passphrase'}
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
>>> str(spam)
"{437: 'passphrase'}"
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
panglima pengawas
sumber
0

Meskipun benar bahwa id(object)mendapatkan alamat objek dalam implementasi CPython default, ini umumnya tidak berguna ... Anda tidak dapat melakukan apa pun dengan alamat dari kode Python murni.

Satu-satunya waktu Anda benar-benar dapat menggunakan alamat adalah dari pustaka ekstensi C ... dalam hal ini sepele untuk mendapatkan alamat objek karena objek Python selalu dibagikan sebagai pointer C.

Dan Lenski
sumber
1
Kecuali Anda menggunakan ctypestoolkit bawaan di Perpustakaan Standar. Dalam hal ini Anda dapat melakukan segala macam hal dengan alamat :)
Brandon Rhodes