Soru Python'da atanmamış bir dizenin bellekteki bir adresi nasıl olabilir?


Birisi bunu bana açıklayabilir mi? Bu yüzden python'da id () komutu ile oynuyordum ve buna rastladım:

>>> id('cat')
5181152
>>> a = 'cat'
>>> b = 'cat'
>>> id(a)
5181152
>>> id(b)
5181152

Bu, bir bölüm dışında benim için biraz mantıklı geliyor: Bir değişkene atamadan önce 'kedi' dizesinde bellekte bir adres var. Muhtemelen bellek adreslemenin nasıl çalıştığını anlamıyorum ama birisi bana bunu açıklayabilir mi, yoksa en azından bana bellek adreslemeyi okumam gerektiğini söyleyebilir mi?

Yani hepsi iyi ve iyi ama bu beni daha da şaşırttı:

>>> a = a[0:2]+'t'
>>> a
'cat'
>>> id(a)
39964224
>>> id('cat')
5181152

Bu bana garip geldi çünkü 'kedi' 5181152 adresli bir dizedir ama yeni bir farklı bir adresi var. Yani iki tane varsa 'kedi' bellekte dizeler neden iki adres yazdırılmıyor id ( 'cat')? Son düşüncem, birleştirmenin adres değişikliğiyle ilgili bir şey yapmasıydı, bu yüzden bunu denedim:

>>> id(b[0:2]+'t')
39921024
>>> b = b[0:2]+'t'
>>> b
'cat'
>>> id(b)
40000896

Kimliklerin aynı olacağını tahmin ederdim ama durum böyle değildi. Düşünceler?


44
2017-08-03 19:07


Menşei


Ayrıca bakınız stackoverflow.com/questions/2519580/... - Zan Lynx
Ve stackoverflow.com/questions/1136826/... - Zan Lynx
Ve stackoverflow.com/questions/1664840/... - Zan Lynx
Ve dahası: stackoverflow.com/questions/1504717/... - Zan Lynx
Whee! stackoverflow.com/questions/1216259/... - Zan Lynx


Cevaplar:


Python, dize değişmezlerini oldukça agresif bir şekilde yeniden kullanır. Bunu yaptığı kurallar uygulamaya bağımlıdır, ancak CPython farkında olduğum iki şeyi kullanır:

  • Python tanımlayıcılarında yalnızca geçerli karakterler içeren dizeler şunlardır: enterne, Yani büyük bir tabloda saklanır ve nerede olurlarsa tekrar kullanılırlar. Yani, nerede kullanırsanız kullanın "cat", her zaman aynı dize nesnesine başvurur.
  • Aynı kod bloğundaki dize değişmezleri, içeriklerine ve uzunluklarına bakılmaksızın yeniden kullanılır. Bir işlevde tüm Gettysburg Adresinin bir dizesini tam olarak yazarsanız, iki kez aynı dize nesnesidir. Ayrı işlevlerde, farklı nesnelerdir: def foo(): return "pack my box with five dozen liquor jugs" def bar(): return "pack my box with five dozen liquor jugs" assert foo() is bar() # AssertionError

Her iki optimizasyon da derleme zamanında yapılır (yani bytecode oluşturulduğunda).

Öte yandan, bir şey gibi chr(99) + chr(97) + chr(116) bir dizedir ifade bu dize değerlendirir "cat". Python gibi dinamik bir dilde, değeri derleme zamanında bilinemez.chr() yerleşik bir işlevdir, ancak yeniden atamış olabilirsiniz. Böylece onun id() bundan farklı "cat". Ancak, bir dizeyi kullanarak internete girmeye zorlayabilirsiniz. intern() işlevi. Böylece:

id(intern(chr(99) + chr(97) + chr(116))) == id("cat")   # True

Başkalarının da belirttiği gibi, dizeler imkansız olduğundan internasyon mümkündür. Değişmek mümkün değil "cat" için "dog", Diğer bir deyişle. Yeni bir dize nesnesi oluşturmanız gerekir, yani aynı dizgiyi işaret eden diğer isimlerin etkileneceği tehlikesi yoktur.

Sadece bir kenara, Python da sadece sabitleri içeren ifadeleri dönüştürür ( "c" + "a" + "t"Aşağıdaki sökme işleminin gösterdiği gibi, derleme süresinde sabitler. Bunlar, yukarıdaki kurallara göre aynı dize nesnelere işaret edecek şekilde optimize edilecektir.

>>> def foo(): "c" + "a" + "t"
...
>>> from dis import dis; dis(foo)
  1           0 LOAD_CONST               5 ('cat')
              3 POP_TOP
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE

52
2017-08-03 19:29



Vay canına, tebrikler, altın rozetler gelmek için zor! Ayrıca, Gettysburg Adresinin bir dizgesini denedim ve Python onu içeri aldılar, bu yüzden oldukça agresif bir şekilde yaptığından eminim. - kindall
Python internette değil herşey dize değişmezleri. Hangileri bir uygulama detayıdır, ama bu davranışın Yalnızca bir Python tanımlayıcısında görünebilecek karakterleri barındıran dahili dize değişmezleri. Gettysburg adresinin içeri girmiş gibi gözükmesi, muhtemelen bir ilgisiz ama çok benzer optimizasyon. - user2357112
Bu çok ilginç! - kindall
Bu yeni bilgiyi iletmek için güncellendi. - kindall


'cat' bir adrese sahipsin çünkü onu iletebilmek için id(). Henüz bir adama bağlı değilsiniz, ancak nesne hala var.

piton kısa dizeleri önbelleğe alır ve yeniden kullanır. Ancak dizeleri birleştirme ile birleştirirseniz, önbelleği arayarak yeniden kullanma girişiminde bulunan kod atlanır.

Dize önbelleğinin iç işleyişinin saf uygulama detayı olduğunu ve güvenilmemesi gerektiğini unutmayın.


47
2017-08-03 19:11





Tüm değerler hafızada bir yerde olmalıdır. Bu nedenle id('cat') bir değer üretir. Sen buna "varolmayan" bir dize diyorsun, ama açıkça var, henüz bir isme atamadı.

Dizeler değişmezdir, bu yüzden tercüman, değişmez tüm örneklerini yapmak gibi akıllıca şeyler yapabilir 'cat' aynı nesne, yani id(a) ve id(b) aynıdır.

Dizeleri çalıştırmak yeni dizeler üretecektir. Bunlar, aynı içeriğe sahip önceki dizgilerle aynı dizeler olabilir veya olmayabilir.

Tüm bu detayların CPython'un uygulama detayları olduğunu ve herhangi bir zamanda değişebileceğini unutmayın. Bu konularda gerçek programlarda endişelenmenize gerek yoktur.


17
2017-08-03 19:13





Python değişkenleri diğer dillerdeki değişkenlerden çok farklıdır (diyelim, C).

Diğer birçok dilde, bir değişken bellekteki bir konum için bir addır. Bu dillerde, farklı türdeki değişkenler farklı konumlara işaret edebilir ve aynı yere birden çok isim verilebilir. Çoğunlukla, belirli bir bellek konumu zaman zaman veri değişimlerine sahip olabilir. Ayrıca hafıza yerlerine dolaylı olarak başvurmanın yolları da vardır (int *p adres içerecektir ve bu adreste bellek konumunda bir tamsayı vardır.) Ancak gerçek bir konum, bir değişken başvurusu değişemez; Değişken olduğu konum. Bu dillerdeki bir değişken atama etkili bir şekilde "Bu değişkenin yerini arayın ve bu verileri bu konuma kopyalayın"

Python bu şekilde çalışmıyor. Python'da, gerçek nesneler bazı bellek konumuna gider ve değişkenler konumlar için etiketler gibidir. Python, saklanan değerleri değişkenleri nasıl yönettiğinden ayrı bir şekilde yönetir. Esas olarak, python'daki bir ödev "Bu değişken için bilgiyi arayın, zaten atıfta bulunduğu yeri unutun ve bunu bu yeni konumla değiştirin" anlamına gelir. Hiçbir veri kopyalanmadı.

Python gibi çalışan öncü bir ortak özellik (daha önce bahsettiğimiz ilk türden farklı olarak), bazı nesnelerin özel bir şekilde yönetilmesi; özdeş değerler ön belleğe alınmazlar ve böylece çok kolay bir şekilde karşılaştırılabilirler (aynı adrese sahiplerse, eşittirler). Bu süreç denir interning; Dinamik olarak oluşturulmuş dizeler olmamasına rağmen, tüm python dizgisi değişmezleri (birkaç başka türe ek olarak) dahil edilir.

Tam kodunuzda, semantik diyalog şöyle olur:

# before anything, since 'cat' is a literal constant, add it to the intern cache
>>> id('cat') # grab the constant 'cat' from the intern cache and look up 
              # it's address
5181152
>>> a = 'cat' # grab the constant 'cat' from the intern cache and 
              # make the variable "a" point to it's location 
>>> b = 'cat' # do the same thing with the variable "b"
>>> id(a) # look up the object "a" currently points to, 
          # then look up that object's address
5181152
>>> id(b) # look up the object "b" currently points to, 
          # then look up that object's address
5181152

8
2017-08-03 19:39





Gönderdiğiniz kod, ara nesneler olarak yeni dizeler oluşturur. Bu oluşturulan dizeler sonunda orijinallerinizle aynı içeriğe sahiptir. Ara zaman diliminde, orijinal ile tam olarak eşleşmezler ve ayrı bir adreste tutulmalıdırlar.

>>> id('cat')
5181152

Diğerleri yanıtladıkça, bu yönergeleri yayınlayarak, Python VM'nin "cat" dizesini içeren bir dize nesnesi oluşturmasına neden olursunuz. Bu dize nesnesi önbelleğe alınmış ve 5181152 numaralı adreste.

>>> a = 'cat'
>>> id(a)
5181152

Yine, "cat" içeren 5181152 numaralı önbelleğe alınmış bu string nesnesine atıfta bulunulmuştur.

>>> a = a[0:2]
>>> id(a)
27731511

Programınızın değiştirilmiş haliyle bu noktada, iki küçük dize nesnesi oluşturdunuz: 'cat' ve 'ca'. 'cat' önbellekte hala var. Dizesi a karakterleri karakterleri içeren farklı ve muhtemelen yeni bir dize nesnesidir. 'ca'.

>>> a = a + 't'
>>> id(a)
39964224

Şimdi başka bir yeni dize nesnesi oluşturdunuz. Bu nesne dizgenin birleşimidir 'ca' adres 27731511 ve dize 't'. Bu birleştirme önceden önbelleğe alınmış dizeyle eşleşiyor 'cat'. Python bu durumu otomatik olarak algılamaz. Kindall belirtildiği gibi, arama ile zorlayabilirsiniz intern() yöntem.

Umarım bu açıklama, adresin hangi aşamada olduğunu aydınlatır. a değişti.

Kodunuz ara durumu içermedi a dize atanmış 'ca'. Cevap hala geçerlidir, çünkü Python yorumlayıcısı ara sonucu tutmak için yeni bir dize nesnesi üretir. a[0:2]Bu ara sonucu bir değişkene atamasanız da olmasa da.


1
2017-08-04 18:24