Python if not == vs if! =


183

Qual è la differenza tra queste due righe di codice:

if not x == 'val':

e

if x != 'val':

Uno è più efficiente dell'altro?

Sarebbe meglio usare

if x == 'val':
    pass
else:

101
Il migliore è quello che puoi leggere, dubito che il collo di bottiglia del tuo programma sarà qui
Thomas Ayoub,

1
Questa domanda mi interessa nel caso "x non in elenco" e "non in elenco"
SomethingSomething

5
@SomethingQualcosa sono interpretati in modo identico.
jonrsharpe,

4
@SomethingSomething di riferimento per il mio commento sopra: stackoverflow.com/q/8738388/3001761
jonrsharpe

1
@SomethingSomething è lo stesso anche per quelli; è come viene interpretata la sintassi, non importa quali siano i due operandi.
jonrsharpe,

Risposte:


229

Usando disper guardare il bytecode generato per le due versioni:

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   

Quest'ultimo ha meno operazioni ed è quindi probabile che sia leggermente più efficiente.


È stato sottolineato nei commenti (grazie, @Quincunx ) che dove hai if foo != barrispetto if not foo == baral numero di operazioni è esattamente lo stesso, è solo che le COMPARE_OPmodifiche e POP_JUMP_IF_TRUEpassano a 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

In questo caso, a meno che non ci fosse una differenza nella quantità di lavoro richiesta per ciascun confronto, è improbabile che tu possa vedere alcuna differenza di prestazioni.


Tuttavia, si noti che le due versioni non saranno sempre logicamente identiche , poiché dipenderà dalle implementazioni di __eq__e __ne__per gli oggetti in questione. Secondo la documentazione del modello di dati :

Non ci sono relazioni implicite tra gli operatori di confronto. La verità di x==ynon implica che x!=ysia falso.

Per esempio:

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


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

Infine, e forse soprattutto, in generale, dove i due sono logicamente identici, x != yè molto più leggibile dinot x == y .


29
In pratica, ogni classe che è __eq__incoerente __ne__è completamente rotta.
Kevin,

8
Si noti che non è sempre vero che not x == yha un'istruzione in più. Quando ho inserito il codice in un if, si è scoperto che entrambi hanno lo stesso numero di istruzioni, solo una aveva POP_JUMP_IF_TRUEe l'altra POP_JUMP_IF_FALSE(questa era l'unica differenza tra loro, a parte l'utilizzo di un diverso COMPARE_OP). Quando ho compilato il codice senza la ifs, ho ottenuto quello che hai.
Justin,

1
Un altro esempio in cui ==e !=non si escludono a vicenda è un'implementazione simile a SQL che coinvolge nullvalori. In SQL nullnon ritorno truea !=rispetto a qualsiasi altro valore, così implementazioni pitone di interfacce SQL possono anche avere lo stesso problema.
Joe,

Sto iniziando a desiderare di non aver fatto riferimento alla possibile differenza tra not ==e !=, sembra essere la parte più interessante della mia risposta! Non penso che questo sia il posto su cui soffermarsi se, perché e quando ciò ha un senso - vedi ad es. Perché Python ha un __ne__metodo operatore anziché solo __eq__?
jonrsharpe,

29

@jonrsharpe ha un'ottima spiegazione di ciò che sta succedendo. Ho pensato di mostrare semplicemente la differenza di tempo quando eseguivo ciascuna delle 3 opzioni 10.000.000 di volte (abbastanza per mostrare una leggera differenza).

Codice utilizzato:

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 i risultati del profiler di cProfile:

inserisci qui la descrizione dell'immagine

Quindi possiamo vedere che c'è una differenza molto piccola di ~ 0,7% tra if not x == 'val':e if x != 'val':. Di questi, if x != 'val':è il più veloce.

Tuttavia, sorprendentemente, possiamo vederlo

if x == 'val':
        pass
    else:

è infatti il ​​più veloce e batte if x != 'val':di ~ 0,3%. Questo non è molto leggibile, ma immagino che se tu volessi un trascurabile miglioramento delle prestazioni, si potrebbe seguire questa strada.


31
Spero che tutti sappiano non agire su queste informazioni! Apportare modifiche illeggibili per un miglioramento dello 0,3% - o anche un miglioramento del 10% - è raramente una buona idea, e questo tipo di miglioramento è molto probabilmente evanescente (e non in senso positivo : lievissimi cambiamenti nel runtime di Python potrebbe eliminare o addirittura invertire qualsiasi guadagno.
Malvolio

1
@Malvolio Inoltre, ci sono diverse implementazioni di Python.
Cees Timmerman,

6

Nel primo Python deve eseguire una delle operazioni in più del necessario (invece di limitarsi a verificare che non sia uguale a esso, deve verificare se non è vero che è uguale, quindi un'altra operazione). Sarebbe impossibile dire la differenza da un'esecuzione, ma se eseguito più volte, il secondo sarebbe più efficiente. Nel complesso userei il secondo, ma matematicamente sono gli stessi


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

Qui puoi vedere che not x == yha un'istruzione in più di x != y. Quindi la differenza di prestazioni sarà molto piccola nella maggior parte dei casi a meno che tu non stia facendo milioni di confronti e anche in questo caso probabilmente non sarà la causa di un collo di bottiglia.


5

Una nota aggiuntiva, poiché le altre risposte hanno risposto alla tua domanda principalmente in modo corretto, è che se una classe definisce __eq__()e non solo __ne__(), allora la COMPARE_OP (!=)eseguirai __eq__()e la annullerai. A quel tempo, la tua terza opzione sarà probabilmente un po 'più efficiente, ma dovrebbe essere considerata solo se AVETE BISOGNO della velocità, poiché è difficile da capire rapidamente.


3

Riguarda il tuo modo di leggerlo. notl'operatore è dinamico, ecco perché puoi applicarlo

if not x == 'val':

Ma !=potrebbe essere letto in un contesto migliore come operatore che fa il contrario di ciò che ==fa.


3
Cosa intendi con " notoperatore dinamico" ?
jonrsharpe,

1
@jonrsharpe Penso che significhi che "non x" chiamerà x .__ bool __ () [python 3 - python 2 usa diverso da zero ] e ripristina il risultato (vedi docs.python.org/3/reference/datamodel.html#object. __bool__ )
jdferreira,

1

Voglio espandere il mio commento di leggibilità sopra.

Ancora una volta, sono completamente d'accordo con la leggibilità prevalente su altre preoccupazioni (insignificanti).

Quello che vorrei sottolineare è che il cervello interpreta "positivo" più velocemente di quanto non lo sia "negativo". Ad esempio, "stop" vs. "non andare" (un esempio piuttosto scadente a causa della differenza nel numero di parole).

Quindi una scelta:

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

è preferibile all'equivalente dal punto di vista funzionale:

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

Una minore leggibilità / comprensibilità porta a più bug. Forse non nella codifica iniziale, ma la manutenzione (non intelligente come te!) Cambia ...


1
il cervello interpreta "positivo" più velocemente di quanto non lo sia "negativo" per esperienza o hai letto studi a riguardo? Sto solo chiedendo perché a seconda del codice in (fare questo) o (farlo), trovo a! = B più facile da capire.
Lafferc,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.