Python buforuje liczby całkowite z zakresu [-5, 256]
, więc oczekuje się, że liczby całkowite w tym zakresie są również identyczne.
To, co widzisz, to kompilator Pythona optymalizujący identyczne literały, gdy jest częścią tego samego tekstu.
Po wpisaniu w Pythonie shell każda linia jest zupełnie inna wypowiedź, analizowany w innym momencie, a więc:
>>> a = 257
>>> b = 257
>>> a is b
False
Ale jeśli umieścisz ten sam kod do pliku:
$ echo 'a = 257
> b = 257
> print a is b' > testing.py
$ python testing.py
True
Dzieje się to za każdym razem, gdy analizator składowy ma szansę przeanalizować, gdzie używane są literały, na przykład podczas definiowania funkcji w interaktywnym interprerze:
>>> 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)
Zobacz, jak skompilowany kod zawiera pojedynczą stałą dla 257
.
Podsumowując, kompilator kodu bajtowego w języku Python nie jest w stanie wykonać ogromnych optymalizacji (takich jak języki statyczne), ale robi więcej, niż myślisz. Jedną z nich jest analiza użycia literałów i unikanie ich powielania.
zauważyć, że nie ma do czynienia z pamięci podręcznej, ponieważ działa również dla pływaków, które nie mają skrzynki:
>>> a = 5.0
>>> b = 5.0
>>> a is b
False
>>> a = 5.0; b = 5.0
>>> a is b
True
Dla bardziej złożonych literałów, jak krotki, że „nie praca ":
>>> a = (1,2)
>>> b = (1,2)
>>> a is b
False
>>> a = (1,2); b = (1,2)
>>> a is b
False
Ale literały wewnątrz krotki są wspólne:
>>> 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
Jeśli chodzi o to, dlaczego widzisz, że dwa PyInt_Object
zostały stworzone, powinienem zgadnąć, że robi się to, aby uniknąć dosłownego porównania. Na przykład, liczba 257
może być wyrażony przez wiele literałach:
>>> 257
257
>>> 0x101
257
>>> 0b100000001
257
>>> 0o401
257
Parser ma dwie możliwości:
- Konwersja literały na typowe zasady przed utworzeniem całkowitą i sprawdzić, czy literałami są równowartość. następnie utwórz pojedynczy obiekt integer.
- Utwórz obiekty całkowite i zobacz, czy są one równe. Jeśli tak, zachowaj tylko jedną wartość i przypisz ją do wszystkich literałów, w przeciwnym razie masz już liczby całkowite do przypisania.
Prawdopodobnie parser Pythona używa drugiego podejścia, które pozwala uniknąć przepisywania kodu konwersji, a także jest łatwiejsze do rozszerzenia (na przykład działa również z elementami pływającymi).
Odczytywanie pliku Python/ast.c
, funkcja, która analizuje wszystkie numery to parsenumber
, który nazywa PyOS_strtoul
aby uzyskać wartość całkowitą (na intgers) i ostatecznie wywołuje PyLong_FromString
:
x = (long) PyOS_strtoul((char *)s, (char **)&end, 0);
if (x < 0 && errno == 0) {
return PyLong_FromString((char *)s,
(char **)0,
0);
}
Jak widać tutaj parser robi , a nie sprawdzić, czy już znalazł liczbę całkowitą o podanej wartości, a to wyjaśnia, dlaczego widzisz, że dwa obiekty int są tworzone, i to również oznacza, że moje przypuszczenie było poprawne: parser jodły t tworzy stałe, a dopiero później optymalizuje kod bajtowy, aby użyć tego samego obiektu dla równych stałych.
Kod, który wykonuje tę kontrolę musi być gdzieś w Python/compile.c
lub Python/peephole.c
, ponieważ są to pliki, które przekształcają AST w kod bajtowy.
W szczególności, funkcja compiler_add_o
wydaje się być tą, która to robi. Jest to komentarz w 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;
więc wydaje się compiler_add_o
służy do wstawiania stałych dla funkcji/lambda itp Funkcja compiler_add_o
przechowuje stałe do dict
obiektu, a od tego natychmiast wynika, że równe stałe spadnie w tym samym gnieździe, dając w wyniku pojedynczą stałą w końcowym bajtodzie.
Czy próbujesz zrozumieć, jak działa źródło Pythona, czy też próbujesz zrozumieć, jaki jest wynik kodu napisanego w Pythonie? Ponieważ wynik kodu napisanego w Pythonie brzmi: "to jest szczegół implementacji, nigdy nie polegaj na tym, co się dzieje lub nie dzieje". – BrenBarn
Nie zamierzam polegać na szczegółach wdrożenia. Jestem ciekawy i próbuję włamać się do kodu źródłowego. – felix021
Powiązane: [Python "jest" operator zachowuje się nieoczekiwanie z liczb całkowitych] (http://stackoverflow.com/questions/306313/python-is-operator-behaves-nieoczekiwanie-z-rozmiarów) – Blckknght