Python aralıktaki tam sayıları önbelleğe alır [-5, 256]
, bu nedenle bu aralıktaki tam sayıların da aynı olması beklenir.
Gördüğünüz, Python derleyicisinin aynı metnin bir parçası olduğunda aynı değişmez değerleri optimize ediyor.
Python kabuğuna yazarken, her satır tamamen farklı bir ifadedir, farklı bir anda ayrıştırılır, böylece:
>>> a = 257
>>> b = 257
>>> a is b
False
Ancak aynı kodu bir dosyaya koyarsanız:
$ echo 'a = 257
> b = 257
> print a is b' > testing.py
$ python testing.py
True
Bu, ayrıştırıcının değişmez değerlerin nerede kullanıldığını analiz etme şansı olduğunda, örneğin etkileşimli yorumlayıcıda bir işlev tanımlarken gerçekleşir:
>>> def test():
... a = 257
... b = 257
... print a is b
...
>>> dis.dis(test)
2 0 LOAD_CONST 1 (257)
3 STORE_FAST 0 (a)
3 6 LOAD_CONST 1 (257)
9 STORE_FAST 1 (b)
4 12 LOAD_FAST 0 (a)
15 LOAD_FAST 1 (b)
18 COMPARE_OP 8 (is)
21 PRINT_ITEM
22 PRINT_NEWLINE
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
>>> test()
True
>>> test.func_code.co_consts
(None, 257)
Derlenen kodun 257
.
Sonuç olarak, Python bayt kodu derleyicisi büyük optimizasyonlar (statik olarak yazılmış diller gibi) gerçekleştiremez, ancak düşündüğünüzden fazlasını yapar. Bunlardan biri, değişmezlerin kullanımını analiz etmek ve bunları kopyalamaktan kaçınmaktır.
Bunun önbellekle bir ilgisi olmadığını unutmayın, çünkü önbelleği olmayan kayan numaralar için de çalışır:
>>> a = 5.0
>>> b = 5.0
>>> a is b
False
>>> a = 5.0; b = 5.0
>>> a is b
True
Tuples gibi daha karmaşık değişmez değerler için, "çalışmaz":
>>> a = (1,2)
>>> b = (1,2)
>>> a is b
False
>>> a = (1,2); b = (1,2)
>>> a is b
False
Ancak demet içindeki değişmez değerler paylaşılır:
>>> a = (257, 258)
>>> b = (257, 258)
>>> a[0] is b[0]
False
>>> a[1] is b[1]
False
>>> a = (257, 258); b = (257, 258)
>>> a[0] is b[0]
True
>>> a[1] is b[1]
True
(Sabit katlamanın ve gözetleme deliği iyileştiricisinin, hata düzeltme sürümleri arasında bile davranışı değiştirebileceğini unutmayın, bu nedenle örnekler geri döner True
veya False
temelde keyfi olur ve gelecekte değişecektir).
Neden ikisinin PyInt_Object
yaratıldığını gördüğünüze gelince , bunun birebir karşılaştırmayı önlemek için yapıldığını tahmin ediyorum . örneğin, sayı 257
birden çok değişmez değerle ifade edilebilir:
>>> 257
257
>>> 0x101
257
>>> 0b100000001
257
>>> 0o401
257
Ayrıştırıcının iki seçeneği vardır:
- Tam sayıyı oluşturmadan önce değişmez değerleri bazı ortak tabana dönüştürün ve değişmez değerlerin eşdeğer olup olmadığına bakın. daha sonra tek bir tamsayı nesnesi oluşturun.
- Tamsayı nesnelerini oluşturun ve eşit olup olmadıklarını görün. Evet ise, yalnızca tek bir değer tutun ve bunu tüm değişmez değerlere atayın, aksi takdirde atanacak tam sayılara zaten sahipsiniz.
Muhtemelen Python ayrıştırıcısı, dönüşüm kodunu yeniden yazmaktan kaçınan ve ayrıca genişletmesi daha kolay olan ikinci yaklaşımı kullanır (örneğin, kayan değerlerle de çalışır).
Okuma Python/ast.c
dosya, tüm sayılar ayrıştırır işlevini parsenumber
çağırır PyOS_strtoul
(intgers için) tam sayı değeri elde etmek için sonunda çağırır PyLong_FromString
:
x = (long) PyOS_strtoul((char *)s, (char **)&end, 0);
if (x < 0 && errno == 0) {
return PyLong_FromString((char *)s,
(char **)0,
0);
}
Eğer ayrıştırıcı yok burada görebileceğiniz gibi değil zaten belirli bir değeri olmayan bir tamsayı bulundu ve iki int nesneleri oluşturulur görüyoruz ve benim tahminim doğru olduğunu bu da aracı neden bu kadar açıklıyor olmadığını kontrol edin: ayrıştırıcı ilk sabitleri yaratır ve ancak daha sonra bayt kodunu aynı nesneyi eşit sabitler için kullanacak şekilde optimize eder.
Bu denetimi yapan kod , AST'yi bayt koduna dönüştüren dosyalar olduğu için Python/compile.c
veya içinde bir yerde olmalıdır Python/peephole.c
.
Özellikle, compiler_add_o
işlev bunu yapan gibi görünüyor. Şu yorum var compiler_lambda
:
/* Make None the first constant, so the lambda can't have a
docstring. */
if (compiler_add_o(c, c->u->u_consts, Py_None) < 0)
return 0;
Bu nedenle compiler_add_o
, işlevler / lambdalar vb. İçin sabitler eklemek için kullanılmış gibi görünüyor . compiler_add_o
İşlev, sabitleri bir dict
nesneye kaydeder ve bundan hemen sonra eşit sabitlerin aynı yuvaya düşmesi sonucu son bayt kodunda tek bir sabit ortaya çıkar.