operator " is " zachowuje się nieoczekiwanie z liczbami całkowitymi
Dlaczego następujące czynności zachowują się nieoczekiwanie w Pythonie?
>>> a = 256
>>> b = 256
>>> a is b
True # This is an expected result
>>> a = 257
>>> b = 257
>>> a is b
False # What happened here? Why is this False?
>>> 257 is 257
True # Yet the literal numbers compare properly
Używam Pythona 2.5.2. Testując różne wersje Pythona, wydaje się, że Python 2.3.3 pokazuje powyższe zachowanie między 99 a 100.
Na podstawie powyższego mogę założyć, że Python jest wewnętrznie zaimplementowany w taki sposób, że "małe" liczby całkowite są przechowywane w inny sposób niż większe liczby całkowite i operator is
może rozróżnić. Skąd ta nieszczelna abstrakcja? Jaki jest lepszy sposób porównywania dwa dowolne obiekty, aby zobaczyć, czy są takie same, gdy nie wiem z góry, czy są liczbami, czy nie?
11 answers
Spójrz na to:
>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828
EDIT: oto co znalazłem w dokumentacji Pythona 2, "Plain Integer Objects" (to samo dotyczy Pythona 3):
Obecna implementacja zachowuje tablica obiektów integer dla wszystkich liczb całkowitych od -5 do 256, gdy Utwórz int w tym zakresie właściwie po prostu wróć do odniesienia do istniejący obiekt. Tak powinno być możliwość zmiany wartości 1. I podejrzenie zachowania pytona w ta sprawa jest nieokreślona. :-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-31 18:45:36
Operator Pythona "is" zachowuje się nieoczekiwanie z liczbami całkowitymi?
Podsumowując-podkreślę: nie używaj is
do porównywania liczb całkowitych.
To nie jest zachowanie, co do którego powinieneś mieć jakiekolwiek oczekiwania.
Zamiast tego użyj ==
i !=
, aby porównać odpowiednio równość i nierówność. Na przykład:
>>> a = 1000
>>> a == 1000 # Test integers like this,
True
>>> a != 5000 # or this!
True
>>> a is 1000 # Don't do this! - Don't use `is` to test integers!!
False
Wyjaśnienie
Aby to wiedzieć, musisz wiedzieć, co następuje.
Po Pierwsze, co robi Czy? Jest operatorem porównawczym. Z dokumentacji :
Operatory
is
iis not
sprawdzają tożsamość obiektu:x is y
jest prawdą wtedy i tylko wtedy, gdy X i y są tym samym obiektem.x is not y
daje odwrotna wartość prawdy.
I tak Poniższe są równoważne.
>>> a is b
>>> id(a) == id(b)
id
Zwraca "tożsamość" obiektu. Jest to liczba całkowita (lub długa integer), która jest gwarantujemy, że będzie unikalny i stały dla tego obiektu w trakcie jego życia. Dwa obiekty o nie nakładających się na siebie życiach mogą mieć tę samą wartośćid()
.
Zauważ, że fakt, że id obiektu w CPython (referencyjnej implementacji Pythona) jest lokalizacją w pamięci, jest szczegółem implementacji. Inne implementacje Pythona (takie jak Jython lub IronPython) mogą łatwo mieć inną implementację dla id
.
Więc jaki jest przypadek użycia is
? PEP8 opisuje :
Porównania do singletonów jak
None
powinny być zawsze wykonywaneis
lubis not
, nigdy operatorów równości.
Pytanie
Zadajesz i odpowiadasz następujące pytanie (z kodem):
Dlaczego następujące czynności zachowują się nieoczekiwanie w Pythonie?
>>> a = 256 >>> b = 256 >>> a is b True # This is an expected result
To nie oczekiwany rezultat. Dlaczego jest to oczekiwane? Oznacza to tylko, że liczby całkowite o wartości 256
odwołuje się zarówno a
, jak i b
są tą samą instancją integer. Liczby całkowite są niezmienne w Pythonie, więc nie mogą się zmieniać. Nie powinno to mieć wpływu na żaden kod. Nie należy się tego spodziewać. Jest to jedynie szczegół wdrożenia.
Ale być może powinniśmy się cieszyć, że nie ma nowej osobnej instancji w pamięci za każdym razem, gdy podajemy wartość równą 256.
>>> a = 257 >>> b = 257 >>> a is b False # What happened here? Why is this False?
Wygląda na to, że mamy teraz dwie oddzielne instancje liczb całkowitych o wartości 257
w pamięć. Ponieważ liczby całkowite są niezmienne, marnuje to pamięć. Miejmy nadzieję, że nie marnujemy tego dużo. Prawdopodobnie nie. Ale to zachowanie nie jest gwarantowane.
Wygląda na to, że Twoja implementacja Pythona stara się być inteligentna i nie tworzyć redundantnie wycenianych liczb całkowitych w pamięci, chyba że musi. Wydajesz się wskazywać, że używasz referentnej implementacji Pythona, którą jest CPython. Dobre dla Cpythona.>>> 257 is 257 True # Yet the literal numbers compare properly
It might be even better jeśli CPython mógłby to zrobić globalnie, jeśli mógłby to zrobić tanio (jak byłby koszt w wyszukiwaniu), być może Inna implementacja mogłaby to zrobić.
Ale jeśli chodzi o wpływ na kod, nie powinieneś dbać o to, czy liczba całkowita jest konkretną instancją liczby całkowitej. Powinieneś dbać tylko o to, jaka jest wartość tej instancji i używałbyś do tego zwykłych operatorów porównania, np. ==
.
Co is
robi
is
sprawdza, czy id
dwóch obiektów są takie same. W CPython, the id
jest lokalizacją w pamięci, ale może to być inny jednoznacznie identyfikujący numer w innej implementacji. Aby dodać to kodem:
>>> a is b
Jest tym samym co
>>> id(a) == id(b)
Dlaczego mielibyśmy używać is
?
Może to być bardzo szybkie sprawdzenie w stosunku do powiedzmy, sprawdzanie, czy dwa bardzo długie ciągi są równe. Ale ponieważ odnosi się to do wyjątkowości obiektu, mamy więc ograniczone przypadki użycia dla niego. W rzeczywistości, głównie chcemy go użyć, aby sprawdzić, czy None
, który jest singletonem (jedyną instancją istniejącą w jednym miejscu w pamięci). Możemy tworzyć inne singletony, jeśli istnieje możliwość ich połączenia, co możemy sprawdzić za pomocą is
, ale są one stosunkowo rzadkie. Oto przykład (będzie działał w Pythonie 2 i 3) np.
SENTINEL_SINGLETON = object() # this will only be created one time.
def foo(keyword_argument=None):
if keyword_argument is None:
print('no argument given to foo')
bar()
bar(keyword_argument)
bar('baz')
def bar(keyword_argument=SENTINEL_SINGLETON):
# SENTINEL_SINGLETON tells us if we were not passed anything
# as None is a legitimate potential argument we could get.
if keyword_argument is SENTINEL_SINGLETON:
print('no argument given to bar')
else:
print('argument to bar: {0}'.format(keyword_argument))
foo()
Który drukuje:
no argument given to foo
no argument given to bar
argument to bar: None
argument to bar: baz
I tak widzimy, z is
i sentinel, jesteśmy w stanie rozróżnić, kiedy bar
jest wywoływana bez argumentów, a kiedy jest wywoływana z None
. Są to podstawowe use-cases for is
- do a nie użyj go do sprawdzenia równości liczb całkowitych, ciągów, krotek lub innych podobnych rzeczy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-09 20:02:03
To zależy od tego, czy chcesz zobaczyć, czy 2 rzeczy są równe, czy ten sam obiekt.
is
sprawdza, czy są tym samym obiektem, a nie tylko równym. Małe ints prawdopodobnie wskazują na to samo miejsce pamięci dla wydajności przestrzeni
In [29]: a = 3
In [30]: b = 3
In [31]: id(a)
Out[31]: 500729144
In [32]: id(b)
Out[32]: 500729144
Powinieneś użyć ==
do porównania równości dowolnych obiektów. Zachowanie można określić za pomocą atrybutów __eq__
i __ne__
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-24 02:20:18
Jak można sprawdzić w pliku źródłowym intobject.c, Python buforuje małe liczby całkowite dla wydajności. Za każdym razem, gdy tworzysz odniesienie do małej liczby całkowitej, odnosisz się do buforowanej małej liczby całkowitej, a nie do nowego obiektu. 257 nie jest małą liczbą całkowitą, więc jest obliczana jako inny obiekt.
Lepiej jest użyć ==
do tego celu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-24 02:20:25
jestem spóźniony, ale chcesz jakieś źródło z twoją odpowiedzią?*
Dobrą rzeczą w Cpythonie jest to, że możesz zobaczyć źródło tego. Na razie użyję linków do wydania 3.5
; znalezienie odpowiednich 2.x
jest banalne.
W Cpythonie funkcja C-API
zajmująca się tworzeniem nowego obiektu int
jest PyLong_FromLong(long v)
. Opis tej funkcji to:
bieżąca implementacja przechowuje tablicę z obiektów integer dla wszystkich liczb całkowitych od -5 do 256, kiedy tworzysz int w tym zakresie, po prostu odzyskujesz odniesienie do istniejącego obiektu. Więc powinno być możliwe, aby zmienić wartość 1. Podejrzewam, że zachowanie Pythona w tym przypadku jest nieokreślone. :-)
Nie wiem jak wy, ale ja to widzę i myślę: znajdźmy tę tablicę!
Jeśli nie bawiłeś się C
kodem implementującym CPython powinieneś , Wszystko jest dość uporządkowane i czytelne. W naszym przypadku musimy zajrzeć do Objects/
podkatalog {[53] } Z głównego drzewa katalogów kodu źródłowego .
PyLong_FromLong
zajmuje się long
obiektami, więc nie powinno być trudno wydedukować, że musimy zajrzeć do środka longobject.c
. Po zaglądnięciu do środka możesz myśleć, że rzeczy są chaotyczne; są, ale nie bój się, funkcja, której szukamy, jest mrożąca krew w żyłach line 230
czeka, aż to sprawdzimy. Jest to mała funkcja, więc głównym body (z wyłączeniem deklaracji) można łatwo wkleić tutaj:
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;
}
Teraz nie jesteśmy C
master-code-haxxorz ale my też nie jesteśmy głupi, widzimy, że CHECK_SMALL_INT(ival);
podglądając nas wszystkich uwodzicielsko, rozumiemy, że to ma z tym coś wspólnego. sprawdźmy to:
#define CHECK_SMALL_INT(ival) \
do if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { \
return get_small_int((sdigit)ival); \
} while(0)
Więc jest to makro, które wywołuje funkcję get_small_int
Jeśli wartość ival
spełnia warunek:
if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS)
Więc czym są NSMALLNEGINTS
i NSMALLPOSINTS
? Jak odgadniesz makra to nic nie dostaniesz bo to to nie było takie trudne pytanie.. w każdym razie, są tutaj.:
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
Więc naszym warunkiem jest if (-5 <= ival && ival < 257)
wezwanie get_small_int
.
Nie ma innego miejsca do odwiedzenia, ale kontynuujemy naszą podróż patrząc na get_small_int
w całej okazałości (cóż, popatrzymy tylko na jego ciało, ponieważ to były interesujące rzeczy): {]}
PyObject *v;
assert(-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS);
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
Py_INCREF(v);
Dobrze, zadeklaruj PyObject
, potwierdź, że poprzedni warunek jest spełniony i wykonaj przypisanie:
v = (PyObject *)&small_ints[ival + NSMALLNEGINTS];
small_ints
wygląda bardzo podobnie. / align = "left" / . i jest! moglibyśmy po prostu przeczytać tę cholerną dokumentację i od początku byśmy wiedzieli!:
/* 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];
Więc tak, to nasz człowiek. Jeśli chcesz utworzyć nowy int
w zakresie [NSMALLNEGINTS, NSMALLPOSINTS)
, po prostu otrzymasz odniesienie do już istniejącego obiektu, który został wstępnie przydzielony.
Ponieważ odniesienie odnosi się do tego samego obiektu, wystawienie id()
bezpośrednio lub sprawdzenie tożsamości z is
zwróci dokładnie to samo rzecz.
Podczas inicjalizacji w _PyLong_Init
Python chętnie wejdzie w pętlę for do do this for you:
for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) {
// Look me up!
}
Mam nadzieję, że moje wyjaśnienie sprawiło, że teraz wszystko jest jasne.
Ale 257 to 257? Co jest?
W rzeczywistości jest to łatwiejsze do wyjaśnienia, i już próbowałem to zrobić ; wynika to z faktu, że Python uruchomi tę interaktywną wypowiedź:
>>> 257 is 257
Jako pojedynczy blok. Podczas uzupełniania tego stwierdzenia, CPython zobaczy, że masz dwa pasujące literały i użyje tego samego PyLongObject
reprezentującego 257
. Możesz to zobaczyć, jeśli sam wykonasz kompilację i przejrzysz jej zawartość:
>>> codeObj = compile("257 is 257", "blah!", "exec")
>>> codeObj.co_consts
(257, None)
Kiedy CPython wykonuje operację, ładuje dokładnie ten sam obiekt:]}
>>> import dis
>>> dis.dis(codeObj)
1 0 LOAD_CONST 0 (257) # dis
3 LOAD_CONST 0 (257) # dis again
6 COMPARE_OP 8 (is)
Więc is
powróci True
.
* -- postaram się powiedzieć to w bardziej sposób wprowadzający, aby większość mogła podążać za nim.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 23:53:19
Myślę, że Twoje hipotezy są poprawne. Eksperyment z id
(tożsamość obiektu):
In [1]: id(255)
Out[1]: 146349024
In [2]: id(255)
Out[2]: 146349024
In [3]: id(257)
Out[3]: 146802752
In [4]: id(257)
Out[4]: 148993740
In [5]: a=255
In [6]: b=255
In [7]: c=257
In [8]: d=257
In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)
Wydaje się, że liczby <= 255
są traktowane jak literały, a wszystko powyżej jest traktowane inaczej!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-08 10:12:22
Dla obiektów niezmiennej wartości, takich jak int, string lub datetimes, tożsamość obiektu nie jest szczególnie przydatna. Lepiej myśleć o równości. Tożsamość jest zasadniczo szczegółem implementacji obiektów value - ponieważ są one niezmienne, nie ma skutecznej różnicy między posiadaniem wielu referencji do tego samego obiektu, a wieloma obiektami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 01:58:53
is
jest operatorem równości tożsamości (działającym jak id(a) == id(b)
); chodzi o to, że dwie równe liczby niekoniecznie są tym samym obiektem. Ze względu na wydajność niektóre małe liczby całkowite są zapamiętane, więc będą miały tendencję do tego samego (można to zrobić, ponieważ są niezmienne).
PHP ' S ===
operator natomiast opisany jest jako sprawdzający równość i typ: x == y and type(x) == type(y)
zgodnie z komentarzem Paulo Freitasa. To wystarczy dla zwykłych liczb, ale różnią się od is
dla klas, które definiują __eq__
w absurdalny sposób:
class Unequal:
def __eq__(self, other):
return False
PHP najwyraźniej pozwala na to samo dla" Wbudowanych " klas (co mam na myśli zaimplementowane na poziomie C, a nie w PHP). Nieco mniej absurdalnym zastosowaniem może być obiekt timer, który ma inną wartość za każdym razem, gdy jest używany jako liczba. Dlaczego chcesz emulować Visual Basic Now
zamiast pokazywać, że jest to ocena z time.time()
Nie wiem.
Greg Hewgill (OP) dokonał jednego wyjaśnienia komentarz " moim celem jest porównanie tożsamości obiektu, a nie równości wartości. Z wyjątkiem liczb, gdzie chcę traktować tożsamość obiektu tak samo jak równość wartości."
To ma jeszcze inną odpowiedź, ponieważ musimy kategoryzować rzeczy jako liczby lub nie, aby wybrać, czy porównujemy z ==
lub is
. CPython definiuje numer protokołu, w tym PyNumber_Check, ale nie jest to dostępne z samego Pythona.
Możemy spróbować użyć isinstance
z wszystkie znane nam typy liczbowe, ale nieuchronnie byłoby to niekompletne. Moduł types zawiera listę StringTypes, ale nie NumberTypes. Od wersji Python 2.6, wbudowane klasy number mają klasę bazowąnumbers.Number
, ale ma ten sam problem:
import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)
Nawiasem mówiąc, NumPy wytworzy oddzielne instancje małych liczb.
Nie znam odpowiedzi na ten wariant pytania. Przypuszczam, że można teoretycznie użyć ctypes do wywołaniaPyNumber_Check
, ale nawet ta funkcja została omówiona i na pewno nie jest przenośna. Musimy być mniej dokładni co do tego, co teraz testujemy.
W końcu ten problem wynika z tego, że Python nie ma pierwotnie drzewa typów z predykatami takimi jak Scheme number?
, lub Haskella Klasa typu Num . is
sprawdza tożsamość obiektu, a nie równość wartości. PHP ma również kolorową historię, gdzie ===
najwyraźniej zachowuje się jak is
tylko na obiektach W PHP5, ale nie PHP4 . Takie są bóle związane z poruszaniem się między językami (w tym wersjami jednego z nich).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-12 11:52:54
Zdarza się również z łańcuchami:
>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)
Teraz wszystko wydaje się w porządku.
>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)
Tego też się spodziewałem.
>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)
>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)
To nieoczekiwane.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-14 15:53:05
Spójrz tutaj
Bieżąca implementacja przechowuje tablicę obiektów całkowitych dla wszystkich liczby całkowite od -5 do 256, kiedy tworzysz int w tym zakresie, właściwie po prostu Odzyskaj odniesienie do istniejącego obiektu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 12:46:27
Jest jeszcze jedna kwestia, która nie jest wskazywana w żadnej z istniejących odpowiedzi. Python może łączyć dowolne dwie niezmienne wartości, a wstępnie utworzone małe wartości int nie są jedynym sposobem na to. Implementacja Pythona nigdy nie jest gwarantowana aby to zrobić, ale wszystkie robią to dla więcej niż tylko małych wejść.
Po pierwsze, istnieją inne wstępnie utworzone wartości, takie jak empty tuple
, str
, i bytes
, oraz kilka krótkich strun (w Cpythonie 3.6 jest to 256 single-character Latin-1 strings). Na przykład:
>>> a = ()
>>> b = ()
>>> a is b
True
Ale również, nawet nie-Pre-utworzone wartości mogą być identyczne. Rozważmy te przykłady:
>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True
I nie jest to ograniczone do int
wartości:
>>> g, h = 42.23e100, 42.23e100
>>> g is h
True
Oczywiście CPython nie ma wstępnie utworzonej float
wartości dla 42.23e100
. Co tu się dzieje?
Kompilator CPython połączy wartości stałe niektórych typów, takich jakint
, float
, str
, bytes
, w tej samej jednostce kompilacyjnej. Na moduł, cały moduł jest jednostką kompilacyjną, ale w interpreterze interaktywnym każde polecenie jest oddzielną jednostką kompilacyjną. Ponieważ c
i d
są zdefiniowane w oddzielnych instrukcjach, ich wartości nie są scalane. Ponieważ e
i f
są zdefiniowane w tym samym poleceniu, ich wartości są scalane.
Możesz zobaczyć, co się dzieje, demontując kod bajtowy. Spróbuj zdefiniować funkcję, która wykonuje e, f = 128, 128
, a następnie wywołać dis.dis
na niej, a zobaczysz, że istnieje pojedynczy wartość stała (128, 128)
>>> def f(): i, j = 258, 258
>>> dis.dis(f)
1 0 LOAD_CONST 2 ((128, 128))
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (i)
6 STORE_FAST 1 (j)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480
Możesz zauważyć, że kompilator zapisał 128
jako stałą, nawet jeśli nie jest ona używana przez kod bajtowy, co daje ci wyobrażenie o tym, jak mało optymalizuje kompilator Cpythona. Co oznacza, że (niepuste) krotki w rzeczywistości nie kończą się scaleniem:
>>> k, l = (1, 2), (1, 2)
>>> k is l
False
Umieść to w funkcji, dis
to, i spójrz na co_consts
-jest 1
i 2
, dwie (1, 2)
krotki, które mają ten sam 1
i 2
, ale nie są identyczne, i ((1, 2), (1, 2))
krotka, która ma dwie różne równe krotki.
Jest jeszcze jedna optymalizacja, którą wykonuje CPython: string interning. W przeciwieństwie do stałego składania kompilatora, nie jest to ograniczone do literałów kodu źródłowego:
>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True
Z drugiej strony, jest on ograniczony do typu str
i łańcuchów pamięci wewnętrznej typu "ascii compact", "compact" lub "legacy ready", a w wielu przypadkach tylko "ascii compact" zostanie internowany.
W każdym razie, zasady wartości, które muszą być, mogą być lub nie mogą być różne, różnią się w zależności od implementacji i między wersjami tej samej implementacji, a może nawet między uruchomieniami tego samego kodu na tej samej kopii tej samej implementacji.
Dla zabawy warto nauczyć się zasad jednego konkretnego Pythona. Ale nie warto na nich polegać w swoim kodzie. Jedyną bezpieczną zasadą jest:
- nie pisz kodu, który zakłada dwa równe, ale tworzone oddzielnie niezmienne wartości są identyczne.
- nie pisz kodu, który zakłada, że dwie równe, ale oddzielnie utworzone niezmienne wartości są różne.
Lub, innymi słowy, używać tylko is
do testowania udokumentowanych singletonów (jak None
) lub które są tworzone tylko w jednym miejscu w kodzie (jak idiom _sentinel = object()
).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-25 03:48:45