В чем разница между этими двумя строками кода:
if not x == 'val':
и
if x != 'val':
Является ли более эффективным, чем другой?
Было бы лучше использовать
if x == 'val':
pass
else:
В чем разница между этими двумя строками кода:
if not x == 'val':
и
if x != 'val':
Является ли более эффективным, чем другой?
Было бы лучше использовать
if x == 'val':
pass
else:
Используя dis
, посмотрите на байт-код, сгенерированный для двух версий:
not ==
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE
!=
4 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE
Последний имеет меньше операций и, следовательно, может быть немного более эффективным.
Было указано в комментариях (спасибо, @Quincunx), что у вас есть if foo != bar
vs. if not foo == bar
количество операций точно такое же, просто измените COMPARE_OP
и POP_JUMP_IF_TRUE
на POP_JUMP_IF_FALSE
:
not ==
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16
!=
2 0 LOAD_FAST 0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16
В этом случае, если не было разницы в объеме работы, необходимой для каждого сравнения, маловероятно, что вы увидите какую-либо разницу в производительности.
Однако обратите внимание, что две версии не всегда будут логически идентичны, так как это будет зависеть от реализаций __eq__
и __ne__
для рассматриваемых объектов. Per документация по модели данных:
Между операторами сравнения нет подразумеваемых отношений. истина
x==y
не означает, чтоx!=y
является ложным.
Например:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
Наконец, и, возможно, самое главное: в общем случае, если они логически идентичны, x != y
гораздо читабельнее, чем not x == y
.
@jonrsharpe имеет отличное объяснение того, что происходит. Я думал, что просто покажу разницу во времени при запуске каждого из 3 вариантов 10 000 000 раз (достаточно для небольшой разницы, чтобы показать).
Используемый код:
def a(x):
if x != 'val':
pass
def b(x):
if not x == 'val':
pass
def c(x):
if x == 'val':
pass
else:
pass
x = 1
for i in range(10000000):
a(x)
b(x)
c(x)
И результаты профилирования cProfile:
Итак, мы видим, что между if not x == 'val':
и if x != 'val':
существует очень маленькая разница ~ 0,7%. Из них if x != 'val':
является самым быстрым.
Однако, самое удивительное, мы видим, что
if x == 'val':
pass
else:
на самом деле является самым быстрым и превосходит if x != 'val':
на ~ 0,3%. Это не очень читаемо, но я думаю, если бы вы хотели незначительное улучшение производительности, можно было бы спуститься по этому маршруту.
В первом Python должен выполнить еще одну операцию, чем необходимо (вместо того, чтобы просто проверять не равную ей, необходимо проверить, не является ли это истиной, что она равна, а значит, еще одна операция). Было бы невозможно сказать разницу с одним исполнением, но если запустить много раз, вторая будет более эффективной. В целом я бы использовал второй, но математически они были одинаковыми
>>> from dis import dis
>>> dis(compile('not 10 == 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 POP_TOP
11 LOAD_CONST 2 (None)
14 RETURN_VALUE
>>> dis(compile('10 != 20', '', 'exec'))
1 0 LOAD_CONST 0 (10)
3 LOAD_CONST 1 (20)
6 COMPARE_OP 3 (!=)
9 POP_TOP
10 LOAD_CONST 2 (None)
13 RETURN_VALUE
Здесь вы можете видеть, что not x == y
имеет еще одну инструкцию, чем x != y
. Таким образом, разница в производительности будет очень мала в большинстве случаев, если вы не делаете миллионы сравнений, и даже тогда это, вероятно, не станет причиной узкого места.
Дополнительная заметка, так как другие ответы отвечали на ваш вопрос в основном правильно, заключается в том, что если класс определяет только __eq__()
, а не __ne__()
, то ваш COMPARE_OP (!=)
будет запускать __eq__()
и отменять его. В то время ваш третий вариант, вероятно, будет немного более эффективным, но его следует учитывать только в том случае, если вам НУЖНА скорость, так как это трудно понять быстро.
Это о вашем способе чтения. Оператор not
динамичен, поэтому вы можете применить его в
if not x == 'val':
Но !=
может быть прочитан в лучшем контексте как оператор, который делает противоположное тому, что делает ==
.
Я хочу расширить свой комментарий о читаемости выше.
Опять же, я полностью согласен с удобочитаемостью, переопределяющей другие (несущественные) проблемы.
Я бы хотел отметить, что мозг интерпретирует "позитивный" быстрее, чем "отрицательный". Например, "остановить" и "не идти" (довольно скверный пример из-за разницы в количестве слов).
Таким образом, выбор:
if a == b
(do this)
else
(do that)
предпочтительнее функционально-эквивалентного:
if a != b
(do that)
else
(do this)
Менее читаемость/понятность приводит к большему количеству ошибок. Возможно, не в первоначальном кодировании, а в изменениях (не так умных, как вы!)...