5 Soru: Python'da atanmamış bir dizgenin bellekte bir adresi nasıl olabilir?

tarafından oluşturulan soru Sun, Jun 2, 2013 12:00 AM

Birisi bunu bana açıklayabilir mi? Bu yüzden python'da id () komutuyla oynuyordum ve bununla karşılaştı:

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

Bir kısım dışında bana biraz mantıklı geliyor: 'cat' dizgisinin bir değişkene atamadan önce bellekte bir adresi var. Muhtemelen sadece hafıza adreslemenin nasıl çalıştığını anlamıyorum ama birisi bunu bana açıklayabilir veya en azından hafıza adresleme hakkında okumam gerektiğini söyleyebilir mi?

Yani hepsi iyi ve güzel ama bu beni daha da karıştırdı:

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

Bu beni çok garip tuttu çünkü 'kedi' 5181152 adresine sahip bir dize ama yeni a farklı bir adrese sahip. Öyleyse, bellekte iki 'kedi' dizesi varsa, neden kimlik ('kedi') için iki adres yazdırılmıyor? Son düşüncem, birleşmenin adres değişikliğiyle ilgisi olduğu için 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?

    
45
  1. 2011-08-03 20: 15: 17Z
  2. 2011-08-03 20: 20: 44Z
  3. 2011-08-03 20: 20: 59Z
  4. 2011-08-03 20: 21: 21Z
  5. 2011-08-03 20: 21: 41Z
5 Yanıtlar                              5                         

Python string değişmezleri oldukça agresif bir şekilde yeniden kullanır. Bunu yapan kurallar uygulamaya bağlıdır, ancak CPython benim bildiğim iki tanesini kullanır:

  • Python tanımlayıcılarında yalnızca geçerli karakterleri içeren dizeler interned, , büyük bir tabloda saklandıkları ve oluştukları yerlerde yeniden kullanıldıkları anlamına gelir. Yani, nerede "cat" kullanıyor olursanız olun, daima aynı string nesnesine işaret eder.
  • Aynı kod bloğundaki dize değişmezleri, içerik ve uzunluklarına bakılmaksızın yeniden kullanılır. Bir fonksiyondaki tüm Gettysburg Adresinin bir dizgesini hazırlarsanız, iki kez, bu iki kere aynı dizgi 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, bayt kodu üretildiğinde).

Diğer yandan, chr(99) + chr(97) + chr(116) gibi bir şey, "cat" dizesini değerlendiren bir ifadesi dizesidir. Python gibi dinamik bir dilde değeri derleme zamanında bilinemez (chr() yerleşik bir işlevdir, ancak yeniden atamış olabilirsiniz), bu yüzden normalde interned değil. Dolayısıyla id(), "cat"'dan farklı. Bununla birlikte, intern() işlevini kullanarak bir dize yerleştirilmesini zorlayabilirsiniz. Böylece:

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

Diğerlerinin de belirttiği gibi, dizgelerin değişmez olması nedeniyle interning mümkündür. Başka bir deyişle, "cat" ila "dog"'u değiştirmek mümkün değildir. Yeni bir dize nesnesi oluşturmalısınız, bu, yaniaynı dizgeye işaret eden isimler etkilenecektir.

Aynı şekilde, Python da, aşağıdaki demontajın gösterdiği gibi, sadece sabitleri içeren ("c" + "a" + "t" gibi) derleme sırasındaki sabitleri dönüştürür. Bunlar, yukarıdaki kurallara göre aynı dize nesnelerine 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-02-02 22: 58: 40Z
  1. Vay, tebrikler, bu altın rozetleri gelmek zor! Ayrıca, Gettysburg Adresinin bir harfini denedim ve Python bunu kullandı, bu yüzden son derece agresif bir şekilde yaptığına eminim.
    2011-08-08 16: 38: 04Z
  2. Python, tümü string değişmezlerini staj yapmıyor. Hangileri bir uygulama detayıdır, ancak davranışın yalnızca Python tanımlayıcısında görünebilecek karakterleri içeren intern dize değişmezleri . Gettysburg adresi internete benziyorsa, muhtemelen alakasız ama çok benzer bir optimizasyon .
    2017-02-02 21: 09: 37Z
  3. Çok ilginç!
    2017-02-02 21: 11: 09Z
  4. Bu yeni bilgiyi iletmek için güncellendi.
    2017-02-02 21: 21: 13Z

'cat'’un bir adresi var çünkü siz id()’a iletmek için Henüz bir isme bağlamadınız, ancak nesne hala var.

Python kısa dizeleri önbelleğe alır ve yeniden kullanır. Ancak, dizeleri birleştirerek birleştirirseniz, önbelleği arayan ve yeniden kullanmaya çalışan kod atlanır.

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

    
47
2013-11-26 18: 33: 14Z

Tüm değerler bellekte bir yerde bulunmalıdır. Bu yüzden id('cat') bir değer üretiyor. Buna "var olmayan" bir dize diyorsunuz, ancak açık bir şekilde var, henüz bir ada atanmadı.

Dizeler değişmezdir, bu nedenle tercüman, 'cat''un tüm örneklerini aynı nesne yapmak gibi zekice şeyler yapabilir, böylece id(a) ve id(b) aynıdır.

Dizgiler üzerinde çalışmak yeni dizgiler üretecektir. Bunlar, aynı içeriğe sahip önceki dizelerle aynı dizeler olabilir veya olmayabilir.

Tüm bu ayrıntıların CPython'un uygulama ayrıntıları olduğunu ve herhangi bir zamanda değişebileceklerini unutmayın. Bu programlarla gerçek programlarda endişelenmenize gerek yok.

    
17
2011-08-03 19: 20: 08Z

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

Diğer birçok dilde, değişken bellekteki bir konum için bir addır. Bu dillerde, farklı değişken türleri, farklı konum türlerine atıfta bulunabilir ve aynı konuma birden çok ad verilebilir. Çoğunlukla, verilen bir hafıza konumu zaman zaman verilerin değişmesine neden olabilir. Ayrıca bellek konumlarına dolaylı olarak başvurmanın da yolları vardır (int *p, adresi içerecektir ve bu adresdeki bellek konumunda bir tamsayı vardır.) değişkeni konumdur. . Bu dillerdeki değişken ataması, "Bu değişkenin konumuna bak ve bu verileri o konuma kopyala" etkili bir şekilde kullanılır.

Python bu şekilde çalışmıyor. P deBunun yerine, gerçek nesneler bazı bellek konumlarına gider ve değişkenler konumlar için etiketlere benzer. Python, depolanan değerleri değişkenleri yönetme biçiminden ayrı bir şekilde yönetir. Temel olarak, python içindeki bir atama “Bu değişken için bilgileri araştırın, halihazırda atıfta bulunduğunuz yeri unutun ve bunu bu yeni konumla değiştirin” anlamına gelir. Hiçbir veri kopyalanmaz.

Python gibi çalışan dillerin ortak bir özelliği (daha önce bahsettiğimiz ilk türden farklı olarak), bazı nesnelerin özel bir şekilde yönetilmesidir; özdeş değerler önbelleklenir, böylece fazladan bellek almazlar ve çok kolay bir şekilde karşılaştırılabilirler (aynı adrese sahiplerse, eşittirler). Bu sürece interning denir; Tüm python string değişmezleri interned (birkaç başka türün yanı sıra) de dahil olmakla birlikte, dinamik olarak oluşturulan dizeler olmayabilir.

Tam kodunuzda, semantik iletişim kutusu şö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
2011-08-03 19: 53: 49Z

Gönderdiğiniz kod, ara nesneler olarak yeni dizeler oluşturur. Bu oluşturulan dizeler sonunda orijinallerinizle aynı içeriğe sahiptir. Ara dönemde, orijinaliyle tam olarak uyuşmazlar ve ayrı bir adreste tutulmaları gerekir.

 
>>> id('cat')
5181152

Diğerlerinin yanıtladığı gibi, bu talimatları vererek, Python VM'nin "cat" dizesini içeren bir dize nesnesi oluşturmasına neden olursunuz. Bu dize nesnesi önbelleğe alınır ve 5181152 no'lu adreste.

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

Yine, "cat" içeren 5181152'deki bu önbelleğe alınmış dize nesnesine atıfta bulunmak için bir a atandı.

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

Programınızın değiştirilmiş sürümündeki bu noktada, iki küçük dize nesnesi oluşturdunuz: 'cat' ve 'ca'. Önbellekte 'cat' hala var. a'un başvurduğu dize, 'ca' karakterlerini içeren farklı ve muhtemelen yeni bir dize nesnesidir.

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

Şimdi başka bir yeni dize nesnesi oluşturdunuz. Bu nesne, 2731511 no'lu adreste 'ca' dizisinin ve 't' dizisinin birleşimidir. Bu birleştirme daha önce önbelleğe alınmış dize 'cat' ile eşleşmiyor. Python bu durumu otomatik olarak algılamıyor. Daha önce de belirtildiği gibi, intern() yöntemiyle aramayı zorlayabilirsiniz.

Umarım bu açıklama a adresinin değiştirildiği adımları aydınlatır.

Kodunuz a dize atanan 'ca' ile ara durumu içermiyordu. Cevap, Python yorumlayıcısının ara sonucu a[0:2] tutmak için yeni bir dize nesnesi ürettiği için geçerlidir, çünkü ara sonucu bir değişkene atadıysanız da alamazsınız.

    
1
2011-08-04 18: 24: 17Z
kaynak yerleştirildi İşte
Diğer sorular