>>> timeit.timeit("'x' in ('x',)")
0.04869917374131205
>>> timeit.timeit("'x' == 'x'")
0.06144205736110564
Также работает для кортежей с несколькими элементами, обе версии кажутся линейно растущими:
>>> 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
Исходя из этого, я думаю, что я должен полностью начать использовать in
везде, а не ==
!
python
performance
python-3.x
python-internals
Маркус Месканен
источник
источник
in
везде вместо==
. Это преждевременная оптимизация, которая вредит читабельности.x ="!foo"
x in ("!foo",)
иx == "!foo"
in
вместо,==
состоит в том, чтобы переключиться на C.Ответы:
Как я уже говорил Дэвиду Волеверу, в этом есть нечто большее, чем кажется на первый взгляд; оба метода отправляются
is
; Вы можете доказать это, делаяПервый может быть настолько быстрым, потому что он проверяет личность.
Чтобы выяснить, почему один из них займет больше времени, давайте проследим выполнение.
Они оба начинаются
ceval.c
,COMPARE_OP
так как это байт-кодЭто извлекает значения из стека (технически это только одно)
и запускает сравнение:
cmp_outcome
это:Это где пути разделены.
PyCmp_IN
Филиал делаетОбратите внимание, что кортеж определяется как
Так что филиал
будет принято, и
*sqm->sq_contains
, что является функцией(objobjproc)tuplecontains
, будет принято.Это делает
Подожди, разве это не то,
PyObject_RichCompareBool
что взяла другая ветвь? Нет, это былоPyObject_RichCompare
.Этот путь к коду был коротким, поэтому он, скорее всего, сводится к скорости этих двух. Давайте сравним.
Путь к коду
PyObject_RichCompareBool
практически сразу заканчивается. ИбоPyObject_RichCompare
этоPy_EnterRecursiveCall
/Py_LeaveRecursiveCall
Комбо не будет приняты в предыдущем пути, но это относительно быстро макросы , которые будут короткое замыкание после увеличения и уменьшения некоторых глобал.do_richcompare
делает:Это делает несколько быстрых проверок, чтобы позвонить,
v->ob_type->tp_richcompare
которыйкоторый делает
А именно, это ярлыки на
left == right
... но только после выполненияВ итоге все пути выглядят примерно так (ручная рекурсивная вставка, развертывание и удаление известных веток).
против
Теперь,
PyUnicode_Check
иPyUnicode_READY
они довольно дешевы, так как они проверяют только пару полей, но должно быть очевидно, что верхний из них - это меньший путь кода, у него меньше вызовов функций, только один оператор switch и он немного тоньше.TL; DR:
Оба отправки
if (left_pointer == right_pointer)
; Разница лишь в том, сколько работы они проделывают, чтобы попасть туда.in
просто делает меньше.источник
Здесь действуют три фактора, которые в совокупности приводят к такому удивительному поведению.
Первое:
in
оператор берет ярлык и проверяет identity (x is y
), прежде чем проверяет равенство (x == y
):Во- вторых , из - за строки в Python интернирование, оба
"x"
s в"x" in ("x", )
будут идентичны:(большое предупреждение: это поведение конкретной реализации!
is
не должна никогда использоваться для сравнения строк , потому что будет давать неожиданные ответы иногда, например"x" * 100 is "x" * 100 ==> False
)В- третьих , как описано в фантастическом ответе Veedrac в ,
tuple.__contains__
(x in (y, )
это примерно эквивалентно(y, ).__contains__(x)
) добирается до точки выполнения проверки идентичности быстрее , чемstr.__eq__
(опять - таки,x == y
это примерно эквивалентноx.__eq__(y)
) делает.Вы можете увидеть доказательства этого, потому что
x in (y, )
это значительно медленнее, чем логически эквивалентныйx == y
:x in (y, )
Дело идет медленнее , потому что, после того , какis
сравнение не удается,in
оператор возвращается к нормальной проверке равенства (то есть, используя==
), так что сравнение занимает примерно столько же время , как==
, что делает всю работу медленнее из - за накладные расходы на создание кортежа выгуливать своих членов и т. д.Следует также отметить , что
a in (b, )
это только быстрее , когдаa is b
:(почему это
a in (b, )
быстрее, чемa is b or a == b
? Я думаю, было бы меньше инструкций виртуальной машины -a in (b, )
всего ~ 3 инструкции, гдеa is b or a == b
будет довольно много инструкций VM)Ответ Veedrac в - https://stackoverflow.com/a/28889838/71522 - идет в гораздо более подробно на что конкретно происходит во время каждого из
==
иin
и хорошо стоит читать.источник
X in [X,Y,Z]
работать правильно безX
,Y
илиZ
необходимость определять методы равенства (или , скорее, равенство по умолчаниюis
, так что избавляет от необходимости звонить__eq__
по объектам, не определяемый пользователя__eq__
иis
быть истинным должно подразумевать значения -equality).float('nan')
может ввести в заблуждение. Это свойство того,nan
что оно не равно себе. Это может изменить время.in
к тестам на членство. Я изменю имя переменной, чтобы уточнить.tuple.__contains__
реализованыtuplecontains
вызовы,PyObject_RichCompareBool
которые сразу возвращаются в случае идентичности.unicode
имеетPyUnicode_RichCompare
под капотом, который имеет такой же ярлык для личности."x" is "x"
это не обязательно будетTrue
.'x' in ('x', )
будет всегдаTrue
, но это может показаться не быстрее, чем==
.