Geç kaldım ama cevabınızla ilgili bir kaynak mı istiyorsunuz? Daha fazla insanın takip edebilmesi için bunu tanıtıcı bir şekilde söyleyeceğim.
CPython ile ilgili iyi bir şey, bunun kaynağını gerçekten görebilmenizdir. 3.5 sürümü için bağlantılar kullanacağım , ancak karşılık gelen 2.x olanları bulmak önemsiz.
CPython'da, yeni bir nesne oluşturmayı işleyen C-API işlevi vardır . Bu işlevin açıklaması:int
PyLong_FromLong(long v)
Geçerli uygulama, -5 ve 256 arasındaki tüm tamsayılar için bir tamsayı nesnesi dizisi tutar, bu aralıkta bir int oluşturduğunuzda, aslında varolan nesneye bir başvuru alırsınız . Bu nedenle 1 değerini değiştirmek mümkün olmalıdır. Bu durumda Python'un davranışının tanımsız olduğundan şüpheleniyorum. :-)
(İtalik harflerle)
Seni bilmiyorum ama bunu görüyorum ve düşünüyorum: Hadi o diziyi bulalım!
Eğer CPython uygulayan C kodu ile fiddled olmadıysanız yapmanız gerekir ; her şey oldukça organize ve okunabilir. Bizim durumumuzda için, içinde bakmak gerekir Objects
alt dizinine ait ana kaynak kodu dizin ağacında .
PyLong_FromLong
long
nesnelerle ilgilenir, bu yüzden içeriye bakmamız gerektiğine karar vermek zor olmamalıdır longobject.c
. İçeri baktıktan sonra işlerin kaotik olduğunu düşünebilirsiniz; aradığımız fonksiyon, 230. satırda ürpertici olup olmadığını kontrol etmemizi bekliyor. Bu ufacık bir işlevdir, bu nedenle ana gövde (beyanlar hariç) buraya kolayca yapıştırılır:
PyObject *
PyLong_FromLong(long ival)
{
// omitting declarations
CHECK_SMALL_INT(ival);
if (ival < 0) {
/* negate: cant write this as abs_ival = -ival since that
invokes undefined behaviour when ival is LONG_MIN */
abs_ival = 0U-(unsigned long)ival;
sign = -1;
}
else {
abs_ival = (unsigned long)ival;
}
/* Fast path for single-digit ints */
if (!(abs_ival >> PyLong_SHIFT)) {
v = _PyLong_New(1);
if (v) {
Py_SIZE(v) = sign;
v->ob_digit[0] = Py_SAFE_DOWNCAST(
abs_ival, unsigned long, digit);
}
return (PyObject*)v;
}
Şimdi biz C master-kodu-haxxorz değiliz ama biz de aptal değiliz CHECK_SMALL_INT(ival);
, hepimize baştan çıkarıcı bakmayı görüyoruz ; bununla bir ilgisi olduğunu anlayabiliriz. Hadi kontrol edelim:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
Bu get_small_int
, değer ival
koşulu karşılarsa işlevi çağıran bir makrodur :
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
Peki ne NSMALLNEGINTS
ve NSMALLPOSINTS
? Makro! İşte bunlar :
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
Bizim durumumuz if (-5 <= ival && ival < 257)
çağrıget_small_int
.
Sonra get_small_int
tüm ihtişamıyla bakalım (iyi, sadece vücuduna bakacağız çünkü ilginç şeylerin olduğu yer):
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
Tamam, bir PyObject
önceki durumun geçerli olduğunu iddia edin ve ödevi yürütün:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_ints
aradığımız diziye çok benziyor ve öyle! Lanet olası belgeleri okuyabilirdik ve her şeyi bilirdik! :
/* Small integers are preallocated in this array so that they
can be shared.
The integers that are preallocated are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
Evet, bu bizim adamımız. int
Aralıkta yeni oluşturmak istediğinizde[NSMALLNEGINTS, NSMALLPOSINTS)
önceden mevcut olan mevcut bir nesneye bir referans geri alırsınız.
Referans aynı nesneyi ifade ettiğinden, id()
doğrudan veren veyais
ettiğinden, üzerinde tamamen aynı şeyi döndürür.
Ancak, ne zaman tahsis edilirler?
_PyLong_Init
Python başlatma sırasında memnuniyetle bir for döngüsü girecektir bunu sizin için yapın:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
Döngü gövdesini okumak için kaynağı kontrol edin!
Umarım açıklamam sana şimdi C şeylerini net bir şekilde yaptı (pun açıkça belli).
Fakat, 257 is 257
? Naber?
Bunu açıklamak daha kolay ve ben bunu zaten yapmaya çalıştım ; Python'un bu etkileşimli ifadeyi tek bir blok olarak yürütmesi nedeniyle:
>>> 257 is 257
Bu ifadenin derlenmesi sırasında, CPython eşleşen iki değişmeziniz olduğunu görecek ve aynı PyLongObject
temsili kullanacaktır 257
. Derlemeyi kendiniz yapar ve içeriğini incelerseniz bunu görebilirsiniz:
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
CPython işlemi gerçekleştirdiğinde, şimdi sadece aynı nesneyi yükleyecek:
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
Böylece is
dönecektir True
.