>>> timeit.timeit("'x' in ('x',)")
0.04869917374131205
>>> timeit.timeit("'x' == 'x'")
0.06144205736110564
Działa również dla krotek z wieloma elementami, obie wersje wydają się rosnąć liniowo:
>>> timeit.timeit("'x' in ('x', 'y')")
0.04866674801541748
>>> timeit.timeit("'x' == 'x' or 'x' == 'y'")
0.06565782838087131
>>> timeit.timeit("'x' in ('y', 'x')")
0.08975995576448526
>>> timeit.timeit("'x' == 'y' or 'x' == 'y'")
0.12992391047427532
W oparciu o to, myślę, że powinienem całkowicie zacząć używać in
wszędzie zamiast ==
!
python
performance
python-3.x
python-internals
Markus Meskanen
źródło
źródło
in
wszędzie zamiast==
. To przedwczesna optymalizacja, która szkodzi czytelności.x ="!foo"
x in ("!foo",)
ix == "!foo"
in
zamiast==
jest przejście do C.Odpowiedzi:
Jak wspomniałem Davidowi Woleverowi, jest w tym coś więcej niż na pierwszy rzut oka; obie metody wysyłają do
is
; możesz to udowodnić, robiącPierwszy może być tak szybki, ponieważ sprawdza tożsamość.
Aby dowiedzieć się, dlaczego jedno trwa dłużej niż drugie, prześledźmy wykonanie.
Oba rozpoczynają się
ceval.c
od,COMPARE_OP
ponieważ jest to związany z nim kod bajtowyWyskakuje wartości ze stosu (technicznie wyskakuje tylko jeden)
i uruchamia porównanie:
cmp_outcome
czy to jest:W tym miejscu ścieżki się rozdzielają.
PyCmp_IN
Oddział robiZauważ, że krotka jest zdefiniowana jako
Więc gałąź
zostanie podjęta i
*sqm->sq_contains
, która jest funkcją(objobjproc)tuplecontains
, zostanie podjęta.To robi
... Czekaj, czy to nie to,
PyObject_RichCompareBool
co wziął drugi oddział? Nie, to byłoPyObject_RichCompare
.Ta ścieżka do kodu była krótka, więc prawdopodobnie sprowadza się do szybkości tych dwóch. Porównajmy.
Ścieżka do kodu
PyObject_RichCompareBool
prawie natychmiast się kończy. PonieważPyObject_RichCompare
tak jestPy_EnterRecursiveCall
/Py_LeaveRecursiveCall
Combo nie zostaną podjęte w poprzedniej ścieżki, ale są stosunkowo szybki makra, że będzie zwarcie po zwiększania i zmniejszania kilka globalnych.do_richcompare
robi:To robi kilka szybkich kontroli na wezwanie
v->ob_type->tp_richcompare
, które jestco robi
Mianowicie te skróty włączone
left == right
... ale dopiero po zrobieniuPodsumowując, wszystkie ścieżki wyglądają mniej więcej tak (ręcznie, rekurencyjnie wstawiając, rozwijając i przycinając znane gałęzie)
vs
Teraz,
PyUnicode_Check
iPyUnicode_READY
są dość tanie, ponieważ tylko sprawdzić kilka pól, ale powinno być oczywiste, że jeden wierzchołek jest mniejszy ścieżka kod, to ma mniej połączeń funkcyjnych tylko jeden switch i jest tylko nieco cieńsze.TL; DR:
Obie wysyłają do
if (left_pointer == right_pointer)
; Różnica polega na tym, ile pracy włożono, aby się tam dostać.in
po prostu robi mniej.źródło
W grę wchodzą trzy czynniki, które łącznie powodują to zaskakujące zachowanie.
Po pierwsze:
in
operator bierze skrót i sprawdza tożsamość (x is y
), zanim sprawdzi równość (x == y
):Po drugie: z powodu ciąg Pythona interning obie
"x"
sw"x" in ("x", )
będą identyczne:(duża uwaga: jest to realizacja specyficzne zachowanie!
is
powinny nigdy być wykorzystywane do porównywania ciągów znaków, ponieważ będzie dać zaskakujące odpowiedzi czasami, na przykład"x" * 100 is "x" * 100 ==> False
)Po trzecie: jak wyszczególniono w fantastycznej odpowiedzi Veedrac ,
tuple.__contains__
(x in (y, )
jest mniej więcej równoważne(y, ).__contains__(x)
) dochodzi do tego, że kontrola tożsamości jest wykonywana szybciej niżstr.__eq__
(znowux == y
jest mniej więcej równoważnax.__eq__(y)
).Możesz zobaczyć dowody na to, ponieważ
x in (y, )
jest znacznie wolniejszy niż logicznie równoważnyx == y
:x in (y, )
Przypadku jest mniejsza, ponieważ pois
nie porównawczym,in
operator wraca do normalnej kontroli równości (czyli z użyciem==
), a więc porównanie odbywa się w tym samym czasie, co==
, co czyni całą operację wolniej z powodu obciążania tworzenia krotki , chodzenie po członkach itp.Zauważ też, że
a in (b, )
jest to szybsze tylko, gdya is b
:(dlaczego jest
a in (b, )
szybszy niża is b or a == b
? Domyślam się, że będzie mniej instrukcji maszyn wirtualnych -a in (b, )
to tylko ~ 3 instrukcje, gdziea is b or a == b
będzie sporo więcej instrukcji maszyn wirtualnych)Odpowiedź Veedrac za - https://stackoverflow.com/a/28889838/71522 - przechodzi w znacznie bardziej szczegółowo na co dokładnie dzieje się podczas każdego
==
iin
i jest warta przeczytania.źródło
X in [X,Y,Z]
działać poprawnie bezX
,Y
lubZ
konieczności definiowania metod równości (czy raczej, równość domyślnym jestis
, więc oszczędza konieczności wzywania__eq__
na obiektach bez zdefiniowanego przez użytkownika__eq__
iis
wartości są prawdziwe powinno oznaczać -równość).float('nan')
jest potencjalnie mylące. Jest właściwością tego,nan
że nie jest sobie równa. To może zmienić czas.in
na testach członkostwa. Zmienię nazwę zmiennej, aby wyjaśnić.tuple.__contains__
jest implementowany przeztuplecontains
które wywołaniaPyObject_RichCompareBool
i które natychmiast zwracają w przypadku tożsamości.unicode
maPyUnicode_RichCompare
pod maską, która ma ten sam skrót tożsamości."x" is "x"
niekoniecznie tak będzieTrue
.'x' in ('x', )
zawsze będzieTrue
, ale może nie wydawać się szybszy niż==
.