121 votes

Pourquoi "if not someobj :" est-il préférable à "if someobj == None :" en Python ?

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

if not someobj:
    #do something

Mais je me demande pourquoi ne pas le faire :

if someobj == None:
    #do something

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

182voto

Sylvain Defresne Points 15231

Dans le premier test, Python essaie de convertir l'objet en un fichier bool s'il ne l'est pas déjà. En gros, nous demandons à l'objet : as-tu un sens ou non ? Ceci est fait en utilisant l'algorithme suivant :

  1. Si l'objet a un __nonzero__ méthode spéciale (tout comme les modules numériques), int y float ), il appelle cette méthode. Elle doit soit retourner un bool qui est ensuite directement utilisée, ou une valeur int valeur qui est considérée comme False si elle est égale à zéro.

  2. Sinon, si l'objet a un __len__ méthode spéciale (comme le font les conteneurs intégrés, list , dict , set , tuple ...), il appelle cette méthode, en considérant un conteneur False s'il est vide (sa longueur est égale à zéro).

  3. Sinon, l'objet est considéré comme True à moins que ce ne soit None auquel cas, il est considéré False .

Dans le deuxième test, l'objet est comparé pour l'égalité à None . Ici, nous demandons à l'objet : "Es-tu égal à cette autre valeur ?" Ceci est fait en utilisant l'algorithme suivant :

  1. Si l'objet a un __eq__ elle est appelée, et la valeur de retour est alors convertie en une méthode bool et utilisé pour déterminer le résultat de la if .

  2. Sinon, si l'objet a un __cmp__ il est appelé. Cette fonction doit retourner un int indiquant l'ordre des deux objets ( -1 si self < other , 0 si self == other , +1 si self > other ).

  3. Dans le cas contraire, les objets sont comparés pour vérifier leur identité (c'est-à-dire s'ils font référence au même objet, ce qui peut être vérifié par la fonction is opérateur).

Il existe un autre test possible en utilisant le is opérateur. On demanderait à l'objet : "Êtes-vous cet objet particulier ?"

D'une manière générale, je recommande 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 de caractères, deux nombres, ...) et de vérifier l'identité uniquement lorsque vous utilisez des valeurs sentinelles ( None qui n'est pas initialisée pour un champ membre par exemple, ou lors de l'utilisation de la fonction getattr ou le __getitem__ méthodes).

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

48voto

S.Lott Points 207588

Il s'agit en fait de deux mauvaises pratiques. Il fut un temps où l'on considérait qu'il était acceptable de traiter None et False comme similaires. Cependant, depuis Python 2.2, ce n'est plus la meilleure politique.

Premièrement, lorsque vous faites un if x o if not x Python doit convertir implicitement les tests de type x en booléen. Les règles relatives à la bool décrivent une série de choses qui sont fausses ; tout le reste est vrai. Si la valeur de x n'était pas proprement booléenne au départ, cette conversion implicite n'est pas vraiment la manière la plus claire de dire les choses.

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

Deuxièmement, vous ne devriez pas vraiment tester avec == None . Vous devez utiliser is None y 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 de singletons y a-t-il ? Cinq : None , True , False , NotImplemented y Ellipsis . Comme il est peu probable que vous utilisiez NotImplemented o Ellipsis et tu ne dirais jamais if x is True (parce que simplement if x est beaucoup plus clair), vous ne testerez jamais que les éléments suivants None .

34voto

badp Points 5036

Parce que None n'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 , () , [] , {} y "" sont toutes différentes de None Ainsi, vos deux extraits de code sont pas équivalent.

En outre, considérez ce qui suit :

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

if object: est pas un contrôle d'égalité. 0 , () , [] , None , {} etc. sont toutes différentes les unes des autres, mais elles évaluer à Faux.

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

foo = bar and spam or eggs

ce qui est un raccourci pour :

if bar:
    foo = spam
else:
    foo = eggs

bien que vous devriez vraiment écrire :

foo = spam if bar else egg

3voto

pi. Points 6026

Si vous demandez

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

le site __nonzéro__ méthode de spam est appelé. Extrait du manuel Python :

__nonzéro__ ( self ) Appelé pour implémenter le test de la valeur de vérité, et l'opération intégrée bool() ; doit retourner Faux ou Vrai, 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."

le site __eq__ méthode de spam est appelé avec l'argument Aucun .

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

2voto

zgoda Points 8549

Ces deux comparaisons ont des objectifs différents. La première vérifie la valeur booléenne de quelque chose, la seconde vérifie l'identité avec une valeur nulle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X