Nesne Bellek Adresine Erişme


168

object.__repr__()Python'da yöntemi çağırdığınızda, bunun gibi bir şey elde edersiniz:

<__main__.Test object at 0x2aba1c0cf890> 

Eğer aşırı eğer hafıza adresine bir tutun almak için herhangi bir yolu var mı __repr__()başka ardından çağırarak, super(Class, obj).__repr__()ve bunu regexing?

Yanıtlar:


208

Python kılavuzu hakkında şöyle demektedir id():

Bir nesnenin "kimliğini" döndür. Bu, yaşamı boyunca bu nesne için benzersiz ve sabit olması garanti edilen bir tamsayıdır (veya uzun tamsayıdır) Üst üste binmeyen ömürleri olan iki nesne aynı id () değerine sahip olabilir. (Uygulama notu: bu nesnenin adresidir.)

CPython'da bu, nesnenin adresi olacaktır. Yine de, diğer Python tercümanları için böyle bir garanti yoktur.

Bir C uzantısı yazıyorsanız, doğrudan nesnelerin adreslerine erişim de dahil olmak üzere Python yorumlayıcısının içlerine tam erişiminiz olduğunu unutmayın.


7
Bu sorunun evrensel bir cevabı değildir ; yalnızca CPython için geçerlidir.
DilithiumMatrix

5
Kendi kendine not: Garanti çoklu işlem için geçerli değildir
Rufus

1
Kullanmanın bazı yolları (içerdiği değeri karşılaştırmak için): forum.freecodecamp.com/t/python-id-object/19207
J.

Bir nesnenin bu bağlamda lifetime(ve ömür boyu ne anlama geldiği) ne anlama gelir overlap/not overlap?
Minh Tran

4
@MinhTran, id nesnenin bellek adresi olduğundan, işlem içinde ve nesne varken benzersizdir. Nesne çöp toplandıktan bir süre sonra bellek yeniden kullanılabilir. Çakışmayan bir ömür, yeni nesne oluşturulduğunda orijinal nesnenin artık var olmadığı anlamına gelir. Dolayısıyla bu sınırlama, bir nesneyi saklamak, serbest bırakmak ve daha sonra eski durumuna döndürmek için bir karma oluşturmak üzere id () işlevini güvenle kullanamayacağınız anlamına gelir.
Joshua Clayton

71

Varsayılan repr'i şu şekilde yeniden uygulayabilirsiniz:

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

1
Bunun eski olduğunu biliyorum, ama yeni bir sınıf yapmak yerine buna ihtiyacınız olduğunda return object.__repr__(self)yapabilirsiniz, hatta yapabilirsinizobject.__repr__(obj)
Artyer

2
@Artyer: Bu yorumun orijinal soru ile ne ilgisi var? Burada gönderilen cevap, orijinal sorunun istediği şekilde adresi yeniden oluşturmaktır. Eğer önerdiğiniz şekilde yaparsanız, mangle yaymak zorunda kalmaz mıydınız?
Rafe

1
Bu bana en iyi cevap gibi görünüyor. Sadece bir nesne () yapmayı deneyin, yazdırın, sonra hex (id (nesne)) yazdırın ve sonuçlar
eşleşsin

@Rafe Cevabınız uzun soluklu bir yöntemdir __repr__ = object.__repr__ve neredeyse işe yaramaz, çünkü bunun işe yaramadığı çeşitli durumlar vardır, örneğin __getattribute__kimliğin geçersiz kılınması veya CPython olmayan bir uygulama bellek konumu. Ayrıca z-doldurmaz, bu nedenle sistem 64bit ise ve sıfırları gerektiği gibi eklemeniz gerekir.
Artyer

@Artyer: Örneğim bir repr'ın nasıl oluşturulacağını gösterir. Sıklıkla özel bilgiler ekleriz (ve hata ayıklamaya yardımcı olduğu için bunun iyi bir kodlama uygulaması olduğunu söyleyebilirim). Bu stili yoğun bir şekilde kullanıyoruz ve ben asla senin en uç durumlarına koşmadım. Onları paylaştığın için teşekkürler!
Rafe


24

Burada, diğer yanıtların hiçbirinde ele alınmayan birkaç sorun var.

İlk olarak, idyalnızca şunu döndürür:

bir nesnenin “kimliği”. Bu, ömrü boyunca bu nesne için benzersiz ve sabit olduğu garanti edilen bir tam sayıdır (veya uzun tam sayıdır). Örtüşmeyen ömürleri olan iki nesne aynı id()değere sahip olabilir .


CPython'da, bu PyObject, yorumlayıcıda object.__repr__görüntülenen aynı şey olan nesneyi temsil eden öğeye işaretçi olur . Ancak bu sadece CPython'un bir uygulama detayıdır, genel olarak Python için geçerli olan bir şey değildir. Jython işaretçilerle ilgilenmez, Java referanslarıyla ilgilenir (ki JVM muhtemelen işaretçiler olarak temsil edilir, ancak bunları göremezsiniz - ve istemezsiniz, çünkü GC onları hareket ettirebilir). PyPy, farklı türlerin farklı türlere sahip olmasına izin verir id, ancak en genel olanı, aradığınız nesne tablosuna sadece bir endekstirid , ki bu açıkça bir işaretçi olmayacaktır. IronPython hakkında emin değilim, ama bu konuda CPython gibi Jython gibi daha fazla şüpheliydim. Yani, çoğu Python uygulamasında, bu konuda ne ortaya çıktığını elde etmenin bir yolu repryoktur ve eğer yaptıysanız, hiçbir faydası yoktur.


Peki ya sadece CPython'u önemsiyorsanız? Ne de olsa bu oldukça yaygın bir durum.

İlk olarak, idbunun bir tamsayı olduğunu fark edebilirsiniz ; * sayı 0x2aba1c0cf890yerine bu dizeyi istiyorsanız, 46978822895760kendiniz biçimlendirmeniz gerekecektir. Kapakları altında, inanıyorum object.__repr__sonuçta kullanıyor printf'ın %pEğer Python yok biçimi, ... ama her zaman bunu yapabilirsiniz:

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

* 3.x'te bu bir int. 2.x'te, intbir işaretçi tutacak kadar büyükse - bu bazı platformlarda imzalı sayı sorunları nedeniyle olmayabilir - longaksi halde.

Bu işaretçilerle bunları yazdırmanın yanı sıra yapabileceğiniz bir şey var mı? Elbette (tekrar, sadece CPython'u önemsediğinizi varsayarsak).

Tüm C API işlevleri bir PyObjectveya ilgili türe işaretçi olur . Bu türler için, PyFoo_Checkbunun gerçekten bir Foonesne olduğundan emin olmak için çağırabilir , sonra ile yayınlayabilirsiniz (PyFoo *)p. Yani, bir C uzantısı yazıyorsanız id, tam olarak ihtiyacınız olan şeydir.

Saf Python kodu yazıyorsanız ne olacak? Sen aynı şeyi aynı işlevleri çağırabilir pythonapidan ctypes.


Sonunda, diğer cevaplardan birkaçı gündeme geldi ctypes.addressof. Burada önemli değil. Bu sadece ctypesgibi nesneler c_int32(ve belki de sağladıkları gibi birkaç bellek arabelleği benzeri nesne numpy) için çalışır. Ve, orada bile, size adresini vermiyor c_int32size C düzeyinde adresini veriyor, değer int32o c_int32tamamladı.

Bununla birlikte, bir şeyin adresine gerçekten ihtiyacınız olduğunu düşünüyorsanız, ilk etapta yerel bir Python nesnesi istemediniz, bir ctypesnesne istediniz .


kimlik önemli olduğunda mutable nesneleri haritalarda / setlerde saklamanın tek yolu bu ...
Enerccio

Diğer kullanımlarına @Enerccio idbir de kesilebilir değerleri tutmak için kullanır dahil- seenya bir cachedict-yok üzerinde herhangi bir şekilde bağlıdır idbir işaretçi olarak, ya da herhangi bir şekilde benzer repr. Tam da bu yüzden bu kod sadece CPython'da çalışmak yerine tüm Python uygulamalarında çalışır.
abarnert

evet, bunun için kullandım id, ama hala java'da bile nesnenin adresini alabilirsin, garip görünüyor (C) Python'da bir yol yok çünkü birisinin hareket etmeyecek sabit gc'si var, böylece adres aynı kalıyor
Enerccio

@Enerccio Ancak, bir nesnenin adresini önbelleğe alınabilir bir değer için kullanmak istemezsiniz; idbir nesne olsun, olmasın, bir adres olsun ya da olmasın. Örneğin, PyPy'de, idgenellikle uygulamadaki bazı gizli tablolara bir dizin olmasına rağmen, CPython'daki bir anahtar kadar yararlıdır, ancak bir işaretçi işe yaramaz, çünkü (Java gibi) nesne taşınabilir hafıza.
abarnert

Neyse @Enerccio, orada olan CPython bir işaretçi almanın bir yolu. Yanıtta açıklandığı gibi, CPython id, bir nesneye ait nesnenin bellekteki konumunun işaretçisi olduğunu uygulamaya özel bir ayrıntı olarak açık bir şekilde belgelemektedir . Bu nedenle, CPython'a özgü kodda işaretçi değeri (yanıtta da açıklandığı gibi neredeyse hiç yapmadığınız) için herhangi bir kullanımınız varsa, belgeyi almanın ve çalışmayı garanti etmenin bir yolu vardır.
abarnert

13

Torsten'e yanıt olarak, addressof()normal bir python nesnesini çağıramıyordum. Ayrıca id(a) != addressof(a),. Bu CPython'da, başka bir şey bilmiyorum.

>>> 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 ile aynı şeyi elde edebilirsiniz

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

Belgeler:

addressof(C instance) -> integer
C örneği dahili arabelleğinin adresini döndürme

CPython içinde, şu anda unutmayın id(a) == ctypes.addressof(a), ancak ctypes.addressofeğer her Python uygulaması için gerçek adresini dönmelidir

  • ctypes desteklenir
  • bellek işaretçileri geçerli bir kavramdır.

Edit : ctypes tercüman bağımsızlığı hakkında bilgi eklendi


13
>>> ctypes'i içe aktar >>> a = (1,2,3) >>> ctypes.addressof (a) Geri izleme (son çağrı son): <module> TypeError içinde "<input>" dosyası, satır 1, dosya: geçersiz tip >>> id (a) 4493268872 >>>

5
Barry ile hemfikirim: Yukarıdaki kod TypeError: invalid typePython 3.4 ile denediğimde ortaya çıkıyor.
Brandon Rhodes


1

Bu eski bir soru olduğunu biliyorum ama hala programlıyorsanız, bugünlerde python 3 ... Aslında bir dize ise, o zaman bunu yapmak için gerçekten kolay bir yol olduğunu buldum:

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

dize dönüştürme de bellekteki konumu etkilemez:

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

0

id(object)Varsayılan CPython uygulamasında nesnenin adresini aldığı doğru olsa da , bu genellikle işe yaramaz ... saf Python kodundan adresle hiçbir şey yapamazsınız .

Adresi gerçekten kullanabileceğiniz tek zaman bir C uzantısı kitaplığından ... bu durumda Python nesneleri her zaman C işaretçisi olarak iletildiği için nesnenin adresini almak önemsizdir.


1
ctypesStandart Kitaplıktaki yerleşik araç setini kullanmadığınız sürece . Bu durumda adres ile her türlü şeyi yapabilirsiniz :)
Brandon Rhodes
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.