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?

Author: Jim Fasarakis Hilliard, 2008-11-20

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. :-)

 308
Author: Cybis,
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 i is 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)

Z dokumentacji :

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 wykonywane is lub is 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.

>>> 257 is 257
True           # Yet the literal numbers compare properly
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.

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.

 64
Author: Aaron Hall,
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__.

 55
Author: JimB,
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.

 36
Author: Angel,
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.

Ale kiedy zostaną przydzielone??

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.

 32
Author: Jim Fasarakis Hilliard,
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!

 18
Author: Amit,
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.

 12
Author: babbageclunk,
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łania PyNumber_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).

 8
Author: Yann Vernier,
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.
 4
Author: sobolevn,
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.

 3
Author: user5319825,
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()).

 2
Author: abarnert,
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