Python se não == vs se! =

183

Qual é a diferença entre essas duas linhas de código:

if not x == 'val':

e

if x != 'val':

Um é mais eficiente que o outro?

Seria melhor usar

if x == 'val':
    pass
else:
lafferc
fonte
101
Quanto melhor for o que você pode ler, duvido que o gargalo do seu programa esteja aqui #
Thomas Ayoub
1
Esta pergunta me interessa no caso "x não está na lista" e "não está na lista"
Alguma
5
@AlgoAlgo são interpretados de forma idêntica.
jonrsharpe
4
@SomethingSomething referência para o meu comentário acima: stackoverflow.com/q/8738388/3001761
jonrsharpe
1
@AlgoAlgo é o mesmo para aqueles também; é como a sintaxe é interpretada, não importa quais são os dois operandos.
jonrsharpe

Respostas:

229

Utilizando dispara olhar o bytecode gerado para as duas versões:

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   

Este último possui menos operações e, portanto, provavelmente será um pouco mais eficiente.


Foi indicado nos comentários (obrigado, @Quincunx ) que onde você tem if foo != barvs. if not foo == baro número de operações é exatamente o mesmo, é só que as COMPARE_OPalterações e as mudanças POP_JUMP_IF_TRUEpara 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

Nesse caso, a menos que haja uma diferença na quantidade de trabalho necessária para cada comparação, é improvável que você veja alguma diferença de desempenho.


No entanto, observe que as duas versões nem sempre serão logicamente idênticas , pois dependerão das implementações __eq__e __ne__dos objetos em questão. Por a documentação do modelo de dados :

Não há relacionamentos implícitos entre os operadores de comparação. A verdade de x==ynão implica quex!=y seja falso.

Por exemplo:

>>> class Dummy(object):
    def __eq__(self, other):
        return True
    def __ne__(self, other):
        return True


>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

Finalmente, e talvez o mais importante: em geral, onde os dois são logicamente idênticos, x != yé muito mais legível do quenot x == y .

jonrsharpe
fonte
29
Na prática, qualquer classe que seja __eq__inconsistente __ne__é completamente quebrada.
24515 Kevin Kevin
8
Observe que nem sempre é verdade que not x == yhá mais uma instrução. Quando eu coloquei o código em um if, descobriu-se que os dois tinham o mesmo número de instruções, apenas um tinha POP_JUMP_IF_TRUEe o outro POP_JUMP_IF_FALSE(essa era a única diferença entre eles, além de usar um diferente COMPARE_OP). Quando compilei o código sem os ifs, obtive o que você conseguiu.
24615 Justin
1
Outro exemplo em que ==e !=não são mutuamente exclusivos é uma implementação semelhante a SQL envolvendo nullvalores. Em SQL nullnão retorna truea !=comparação com qualquer outro valor, então implementações de python de interfaces SQL também podem ter o mesmo problema.
21415 Joe
Estou começando a desejar não ter me referido à possível diferença entre not ==e !=, parece ser a parte mais interessante da minha resposta! Eu não acho que este seja o lugar para pensar se, por que e quando isso faz sentido - veja, por exemplo, por que o Python tem um __ne__método de operador em vez de apenas __eq__?
jonrsharpe
29

@jonrsharpe tem uma excelente explicação do que está acontecendo. Eu pensei em mostrar a diferença no tempo ao executar cada uma das três opções 10.000.000 de vezes (o suficiente para mostrar uma pequena diferença).

Código usado:

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)

E o perfil do cProfile resulta:

insira a descrição da imagem aqui

Portanto, podemos ver que há uma diferença muito pequena de ~ 0,7% entre if not x == 'val':e if x != 'val':. Destes, if x != 'val':é o mais rápido.

Surpreendentemente, porém, podemos ver que

if x == 'val':
        pass
    else:

é de fato o mais rápido e supera if x != 'val':em ~ 0,3%. Isso não é muito legível, mas acho que se você quiser uma melhoria insignificante de desempenho, poderá seguir esse caminho.

Red Shift
fonte
31
Espero que todos saibam não agir com base nessas informações! Fazer alterações ilegíveis para uma melhoria de 0,3% - ou mesmo uma melhoria de 10% - raramente é uma boa idéia, e esse tipo de melhoria provavelmente será evanescente (e não do bom modo : mudanças muito pequenas no tempo de execução do Python poderia eliminar ou até mesmo reverter qualquer ganho.
Malvolio
1
@ Malvolio Além disso, existem diferentes implementações do Python.
amigos estão dizendo sobre
6

No primeiro, o Python precisa executar mais uma operação do que o necessário (em vez de apenas verificar não igual a ele, deve verificar se não é verdade que é igual, portanto, mais uma operação). Seria impossível distinguir a diferença de uma execução, mas se executada muitas vezes, a segunda seria mais eficiente. No geral, eu usaria o segundo, mas matematicamente eles são os mesmos

JediPythonClone
fonte
5
>>> 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

Aqui você pode ver que not x == ytem mais uma instrução que x != y. Portanto, a diferença de desempenho será muito pequena na maioria dos casos, a menos que você esteja fazendo milhões de comparações e mesmo assim isso provavelmente não será a causa de um gargalo.

kylie.a
fonte
5

Uma observação adicional, uma vez que as outras respostas responderam à sua pergunta principalmente corretamente, é que, se uma classe apenas define __eq__()e não __ne__(), então você COMPARE_OP (!=)a executará __eq__()e a negará. Nesse momento, sua terceira opção provavelmente será um pouco mais eficiente, mas só deve ser considerada se você precisar da velocidade, pois é difícil entender rapidamente.

Jacob Zimmerman
fonte
3

É sobre a sua maneira de lê-lo. notoperador é dinâmico, é por isso que você pode aplicá-lo

if not x == 'val':

Mas !=poderia ser lido em um contexto melhor como um operador que faz o oposto do que ==faz.

Himanshu Mishra
fonte
3
O que você quer dizer com " notoperador é dinâmico" ?
Jonrsharpe
1
@ Jonrsharpe Acho que ele quer dizer que "not x" chamará x .__ bool __ () [python 3 - python 2 usa diferente de zero ] e reverterá o resultado (consulte docs.python.org/3/reference/datamodel.html#object. __bool__ )
jdferreira
1

Quero expandir meu comentário de legibilidade acima.

Mais uma vez, concordo plenamente com a legibilidade, substituindo outras preocupações (insignificantes de desempenho).

O que eu gostaria de salientar é que o cérebro interpreta "positivo" mais rapidamente do que "negativo". Por exemplo, "parar" vs. "não seguir" (um exemplo bastante ruim devido à diferença no número de palavras).

Então, dada uma escolha:

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

é preferível ao funcionalmente equivalente:

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

Menos legibilidade / compreensão leva a mais erros. Talvez não na codificação inicial, mas a manutenção (não tão inteligente quanto você!) Muda ...

Alan Jay Weiner
fonte
1
o cérebro interpreta "positivo" mais rápido do que "negativo" é isso por experiência própria ou você leu estudos sobre isso? Só estou perguntando porque, dependendo do código (faça isso) ou (faça isso), acho a! = B mais fácil de entender.
Lafferc