В чем разница между этими двумя строками кода:
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)
Менее читаемость/понятность приводит к большему количеству ошибок. Возможно, не в первоначальном кодировании, а в изменениях (не так умных, как вы!)...