Quelle est la différence entre ces deux lignes de code:
if not x == 'val':
et
if x != 'val':
L'un est-il plus efficace que l'autre?
Serait-il préférable d'utiliser
if x == 'val':
pass
else:
Quelle est la différence entre ces deux lignes de code:
if not x == 'val':
et
if x != 'val':
L'un est-il plus efficace que l'autre?
Serait-il préférable d'utiliser
if x == 'val':
pass
else:
Réponses:
Utilisation dis
pour regarder le bytecode généré pour les deux versions:
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
Ce dernier a moins d'opérations et est donc susceptible d'être légèrement plus efficace.
Il a été souligné dans les commentaires (merci, @Quincunx ) que là où vous avez if foo != bar
vs if not foo == bar
le nombre d'opérations est exactement le même, c'est juste que les COMPARE_OP
changements et les POP_JUMP_IF_TRUE
commutations vers 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
Dans ce cas, à moins qu'il n'y ait une différence dans la quantité de travail requise pour chaque comparaison, il est peu probable que vous voyiez une différence de performance.
Cependant, notez que les deux versions ne seront pas toujours logiquement identiques , car cela dépendra des implémentations de __eq__
et __ne__
pour les objets en question. Selon la documentation du modèle de données :
Il n'y a pas de relations implicites entre les opérateurs de comparaison. La vérité de
x==y
n'implique pas que cex!=y
soit faux.
Par exemple:
>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True
>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True
Enfin, et peut-être le plus important: en général, là où les deux sont logiquement identiques, x != y
est beaucoup plus lisible quenot x == y
.
__eq__
pas cohérente avec __ne__
est complètement cassée.
not x == y
ait une instruction de plus. Lorsque j'ai mis le code dans un if
, il s'est avéré qu'ils avaient tous les deux le même nombre d'instructions, une seule avait POP_JUMP_IF_TRUE
et l'autre POP_JUMP_IF_FALSE
(c'était la seule différence entre eux, à part utiliser un autre COMPARE_OP
). Quand j'ai compilé le code sans le if
s, j'ai ce que vous avez.
==
et !=
ne s'excluent pas mutuellement est une implémentation de type SQL impliquant des null
valeurs. Dans SQL null
ne revient pas true
à !=
par rapport à toute autre valeur, de sorte que les implémentations python des interfaces SQL peuvent également avoir le même problème.
not ==
et !=
, cela semble être la partie la plus intéressante de ma réponse! Je ne pense pas que ce soit l'endroit sur lequel s'attarder si, pourquoi et quand cela a du sens - voir par exemple Pourquoi Python a-t-il une __ne__
méthode d'opérateur au lieu de juste __eq__
?
@jonrsharpe a une excellente explication de ce qui se passe. Je pensais simplement montrer la différence de temps lors de l'exécution de chacune des 3 options 10 000 000 fois (assez pour qu'une légère différence apparaisse).
Code utilisé:
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)
Et les résultats du profileur cProfile:
Nous pouvons donc voir qu'il y a une très infime différence de ~ 0,7% entre if not x == 'val':
et if x != 'val':
. De ceux-ci, if x != 'val':
est le plus rapide.
Cependant, le plus surprenant, nous pouvons voir que
if x == 'val':
pass
else:
est en fait le plus rapide et bat if x != 'val':
d'environ 0,3%. Ce n'est pas très lisible, mais je suppose que si vous vouliez une amélioration négligeable des performances, vous pourriez emprunter cette voie.
Dans le premier, Python doit exécuter une opération de plus que nécessaire (au lieu de vérifier simplement qu'il n'est pas égal, il doit vérifier si ce n'est pas vrai qu'il est égal, donc une opération de plus). Il serait impossible de faire la différence entre une exécution, mais si elle était exécutée plusieurs fois, la seconde serait plus efficace. Dans l'ensemble, j'utiliserais le second, mais mathématiquement, ce sont les mêmes
>>> 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
Ici, vous pouvez voir qu'il y not x == y
a une instruction de plus que x != y
. Ainsi, la différence de performances sera très faible dans la plupart des cas, à moins que vous ne fassiez des millions de comparaisons et même dans ce cas, cela ne sera probablement pas la cause d'un goulot d'étranglement.
Une note supplémentaire, étant donné que les autres réponses ont généralement répondu correctement à votre question, est que si une classe définit uniquement __eq__()
et non __ne__()
, alors vous COMPARE_OP (!=)
l'exécuterez __eq__()
et la nierez. À ce moment-là, votre troisième option sera probablement un peu plus efficace, mais ne devrait être envisagée que si vous avez BESOIN de la vitesse, car elle est difficile à comprendre rapidement.
Il s'agit de votre façon de le lire. not
l'opérateur est dynamique, c'est pourquoi vous pouvez l'appliquer dans
if not x == 'val':
Mais !=
pourrait être lu dans un meilleur contexte comme un opérateur qui fait le contraire de ce que ==
fait.
not
opérateur est dynamique" ?
Je souhaite développer mon commentaire sur la lisibilité ci-dessus.
Encore une fois, je suis entièrement d'accord avec le fait que la lisibilité l'emporte sur d'autres préoccupations (non significatives en termes de performances).
Ce que je voudrais souligner, c'est que le cerveau interprète «positif» plus vite que «négatif». Par exemple, "arrêter" contre "ne pas y aller" (un exemple plutôt moche en raison de la différence de nombre de mots).
Alors donné le choix:
if a == b
(do this)
else
(do that)
est préférable à l'équivalent fonctionnel:
if a != b
(do that)
else
(do this)
Moins de lisibilité / compréhensibilité entraîne plus de bogues. Peut-être pas dans le codage initial, mais la maintenance (pas aussi intelligente que vous!) Change ...