Python if not == vs if! =

В чем разница между этими двумя строками кода:

if not x == 'val':

и

if x != 'val':

Является ли более эффективным, чем другой?

Было бы лучше использовать

if x == 'val':
    pass
else:

Ответ 1

Используя 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.

Ответ 2

@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:

enter image description here

Итак, мы видим, что между if not x == 'val': и if x != 'val': существует очень маленькая разница ~ 0,7%. Из них if x != 'val': является самым быстрым.

Однако, самое удивительное, мы видим, что

if x == 'val':
        pass
    else:

на самом деле является самым быстрым и превосходит if x != 'val': на ~ 0,3%. Это не очень читаемо, но я думаю, если бы вы хотели незначительное улучшение производительности, можно было бы спуститься по этому маршруту.

Ответ 3

В первом Python должен выполнить еще одну операцию, чем необходимо (вместо того, чтобы просто проверять не равную ей, необходимо проверить, не является ли это истиной, что она равна, а значит, еще одна операция). Было бы невозможно сказать разницу с одним исполнением, но если запустить много раз, вторая будет более эффективной. В целом я бы использовал второй, но математически они были одинаковыми

Ответ 4

>>> 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. Таким образом, разница в производительности будет очень мала в большинстве случаев, если вы не делаете миллионы сравнений, и даже тогда это, вероятно, не станет причиной узкого места.

Ответ 5

Дополнительная заметка, так как другие ответы отвечали на ваш вопрос в основном правильно, заключается в том, что если класс определяет только __eq__(), а не __ne__(), то ваш COMPARE_OP (!=) будет запускать __eq__() и отменять его. В то время ваш третий вариант, вероятно, будет немного более эффективным, но его следует учитывать только в том случае, если вам НУЖНА скорость, так как это трудно понять быстро.

Ответ 6

Это о вашем способе чтения. Оператор not динамичен, поэтому вы можете применить его в

if not x == 'val':

Но != может быть прочитан в лучшем контексте как оператор, который делает противоположное тому, что делает ==.

Ответ 7

Я хочу расширить свой комментарий о читаемости выше.

Опять же, я полностью согласен с удобочитаемостью, переопределяющей другие (несущественные) проблемы.

Я бы хотел отметить, что мозг интерпретирует "позитивный" быстрее, чем "отрицательный". Например, "остановить" и "не идти" (довольно скверный пример из-за разницы в количестве слов).

Таким образом, выбор:

if a == b
    (do this)
else
    (do that)

предпочтительнее функционально-эквивалентного:

if a != b
    (do that)
else
    (do this)

Менее читаемость/понятность приводит к большему количеству ошибок. Возможно, не в первоначальном кодировании, а в изменениях (не так умных, как вы!)...