Pourquoi «if not someobj:» est meilleur que «if someobj == None:» en Python?

127

J'ai vu plusieurs exemples de code comme celui-ci:

if not someobj:
    #do something

Mais je me demande pourquoi ne pas faire:

if someobj == None:
    #do something

Y a-t-il une différence? L'un a-t-il un avantage sur l'autre?

fuentesjr
la source
13
Généralement, 'someobj is None' est préférable à 'someobj == None'
Aaron Maenpaa
qu'en est-il du pseudocode si X n'est pas None? Ou X != None?
Charlie Parker
cette question est plus verbeuse que les autres ... bravo ...
alamin

Réponses:

187

Dans le premier test, Python essaie de convertir l'objet en une boolvaleur s'il n'en est pas déjà une. En gros, nous demandons à l'objet: êtes-vous significatif ou non? Cela se fait à l'aide de l'algorithme suivant:

  1. Si l'objet a une __nonzero__méthode spéciale (comme le font les intégrés numériques, intet float), il appelle cette méthode. Il doit soit renvoyer une boolvaleur qui est alors directement utilisée, soit une intvaleur considérée Falsesi égale à zéro.

  2. Dans le cas contraire, si l'objet a une __len__méthode spéciale (comme les conteneurs Encastrements, list, dict, set, tuple, ...), il appelle cette méthode, compte tenu d' un récipient Falsesi elle est vide (longueur est égale à zéro).

  3. Sinon, l'objet est considéré à Truemoins que ce ne soit le Nonecas auquel cas, il est considéré False.

Dans le deuxième test, l’égalité de l’objet est comparée à None. Ici, nous demandons à l'objet: "Êtes-vous égal à cette autre valeur?" Cela se fait à l'aide de l'algorithme suivant:

  1. Si l'objet a une __eq__méthode, il est appelé et la valeur de retour est ensuite convertie en boolvaleur et utilisée pour déterminer le résultat du if.

  2. Sinon, si l'objet a une __cmp__méthode, il est appelé. Cette fonction doit renvoyer un intindiquant l'ordre des deux objets ( -1si self < other, 0si self == other, +1si self > other).

  3. Sinon, les objets sont comparés pour l'identité (c'est-à-dire qu'ils font référence au même objet, comme cela peut être testé par l' isopérateur).

Il existe un autre test possible en utilisant l' isopérateur. Nous demanderions à l'objet: "Êtes-vous cet objet particulier?"

De manière générale, je recommanderais d'utiliser le premier test avec des valeurs non numériques, d'utiliser le test d'égalité lorsque vous souhaitez comparer des objets de même nature (deux chaînes, deux nombres, ...) et de vérifier l'identité uniquement lorsque en utilisant des valeurs sentinelles ( Nonec'est-à-dire non initialisé pour un champ membre par exemple, ou lors de l'utilisation getattrdes __getitem__méthodes ou ).

Pour résumer, nous avons:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True
Sylvain Defresne
la source
4
Bien que techniquement correct, cela n'explique pas que les tuples, listes, dicts, strs, unicodes, ints, floats, etc. aient une valeur non nulle . Il est beaucoup plus courant de s'appuyer sur la valeur de vérité du type intégré que de s'appuyer sur une méthode personnalisée différente de zéro .
ddaa le
50

Ce sont en fait deux mauvaises pratiques. Il était une fois, il était considéré comme acceptable de traiter avec désinvolture None et False comme similaires. Cependant, depuis Python 2.2, ce n'est pas la meilleure politique.

Premièrement, lorsque vous effectuez un test if xou un if not xtype de test, Python doit implicitement se convertir xen booléen. Les règles de la boolfonction décrivent une série de choses qui sont fausses; tout le reste est vrai. Si la valeur de x n'était pas correctement booléenne au départ, cette conversion implicite n'est pas vraiment la façon la plus claire de dire les choses.

Avant Python 2.2, il n'y avait pas de fonction booléenne, donc c'était encore moins clair.

Deuxièmement, vous ne devriez pas vraiment tester avec == None. Vous devez utiliser is Noneet is not None.

Voir PEP 8, Guide de style pour le code Python .

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

Combien y a-t-il de singletons? Cinq: None, True, False, NotImplementedet Ellipsis. Comme il est peu probable que vous utilisiez NotImplementedou Ellipsis, et que vous ne diriez jamaisif x is True (parce que if xc'est beaucoup plus clair), vous ne testerez jamais None.

S.Lott
la source
3
La deuxième forme n'est catégoriquement pas une mauvaise pratique. PEP 8 recommande d'utiliser if x deux fois . D'abord pour les séquences (au lieu d'utiliser len), puis pour True et False (au lieu d'utiliser is). Pratiquement tout le code Python que j'ai vu utilise if x et sinon x.
Antti Rasinen
35

Parce que ce Nonen'est pas la seule chose qui est considérée comme fausse.

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False, 0, (), [], {}Et ""sont toutes différentes None, de sorte que vos deux extraits de code sont pas équivalents.

De plus, considérez ce qui suit:

>>> False == 0
True
>>> False == ()
False

if object:n'est pas un contrôle d'égalité. 0, (), [], None, {}, Etc. sont tous différents les uns des autres, mais ils ont tous évaluent à Faux.

C'est la "magie" derrière les expressions de court-circuit comme:

foo = bar and spam or eggs

qui est un raccourci pour:

if bar:
    foo = spam
else:
    foo = eggs

bien que vous devriez vraiment écrire:

foo = spam if bar else egg
badp
la source
Concernant votre dernière question, elles sont équivalentes.
Sylvain Defresne
Et les deux sont incorrects car "" est faux. Le second devrait lire '("",) ou ("s",)'. Quoi qu'il en soit, les versions modernes de python ont un opérateur ternaire approprié. Ce hack sujet aux erreurs doit être banni.
ddaa le
4

PEP 8 - Guide de style pour le code Python recommande d'utiliser is ou non si vous testez None-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

D'un autre côté, si vous testez plus de None-ness, vous devez utiliser l'opérateur booléen.

Vinko Vrsalovic
la source
3

Si vous demandez

if not spam:
    print "Sorry. No SPAM."

la méthode __nonzero__ de spam est appelée. À partir du manuel Python:

__nonzero__ ( self ) Appelé pour implémenter le test de la valeur de vérité et l'opération intégrée bool (); doit renvoyer False ou True, ou leurs équivalents entiers 0 ou 1. Lorsque cette méthode n'est pas définie, __len __ () est appelée, si elle est définie (voir ci-dessous). Si une classe ne définit ni __len __ () ni __nonzero __ (), toutes ses instances sont considérées comme vraies.

Si vous demandez

if spam == None:
    print "Sorry. No SPAM here either."

la méthode __eq__ de spam est appelée avec l'argument None .

Pour plus d'informations sur les possibilités de personnalisation, consultez la documentation Python à https://docs.python.org/reference/datamodel.html#basic-customization

pi.
la source
2

Ces deux comparaisons servent des objectifs différents. Le premier vérifie la valeur booléenne de quelque chose, le second vérifie l'identité avec la valeur None.

zgoda
la source
1
Pour être absolument correct, le second vérifie -equality- avec une valeur None ... "someobj is None" vérifie l'identité.
Matthew Trevor
0

Pour l'un, le premier exemple est plus court et plus joli. Comme pour les autres articles, ce que vous choisissez dépend également de ce que vous voulez vraiment faire avec la comparaison.

rslite
la source
0

La réponse est "ça dépend".

J'utilise le premier exemple si je considère 0, "", [] et False (liste non exhaustive) comme équivalents à None dans ce contexte.

Matthias Kestenholz
la source
0

Personnellement, j'ai choisi une approche cohérente à travers les langages: je ne fais if (var)(ou équivalent) que si var est déclaré booléen (ou défini comme tel, en C nous n'avons pas de type spécifique). Je préfixe même ces variables avec un b(donc ce serait en bVarfait) pour être sûr que je n'utiliserai pas accidentellement un autre type ici.
Je n'aime pas vraiment le cast implicite en booléen, encore moins quand il y a de nombreuses règles complexes.

Bien sûr, les gens ne seront pas d'accord. Certains vont plus loin, je vois if (bVar == true)dans le code Java à mon travail (trop redondant à mon goût!), D'autres aiment trop la syntaxe compacte, aller while (line = getNextLine())(trop ambigu pour moi).

PhiLho
la source