si A vs si A n'est pas Aucun:

154

Puis-je utiliser:

if A:

au lieu de

if A is not None:

Ce dernier semble tellement verbeux. Y a-t-il une différence?

rbairos
la source

Réponses:

149

La déclaration

if A:

appellera A.__nonzero__()(voir la documentation des noms de méthodes spéciales ) et utilisera la valeur de retour de cette fonction. Voici le résumé:

object.__nonzero__(self)

Appelé à implémenter le test de valeur de vérité et l'opération intégrée bool(); doit renvoyer Falseou True, ou leurs équivalents entiers 0ou 1. Lorsque cette méthode n'est pas définie, __len__()est appelée, si elle est définie, et l'objet est considéré comme vrai si son résultat est différent de zéro. Si une classe ne définit ni __len__()ni __nonzero__(), toutes ses instances sont considérées comme vraies.

D'autre part,

if A is not None:

compare uniquement la référence Aavec Nonepour voir si elle est la même ou non.

Greg Hewgill
la source
4
et A is not Noneest donc plus rapide car il y a beaucoup moins de travail à faire
John La Rooy
40
@gnibbler Pas d'après mes tests. if object(): passest d'environ 0,130 usec par boucle, tandis que d' if object() is not None: passenviron 0,135 usec. Quoi qu'il en soit, vous ne devriez pas utiliser les performances pour choisir entre ces deux, mais plutôt regarder les différences dans leur fonctionnement, car elles ne sont pas équivalentes .
Lauritz V. Thaulow
1
@gnibbler if A is not Nonesemble être plus lent car il s'agit d'une comparaison et doit charger le singleton intégré Nonecomme étape intermédiaire avec laquelle comparer A(jetez un œil au dis.dis()). Corrigez-moi si je me trompe mais if A:semble être plus efficace, dès que vous voulez vraiment tester la valeur de vérité et non l' Noneidentité.
cedbeu
2
@cedbeu, semble dépendre de la valeur de A. J'ai testé maintenant python -m timeit -s"a=0" "if a: pass" "else: pass"est plus rapide que python -m timeit -s"a=0" "if a is None: pass" "else: pass"mais python -m timeit -s"a=1" "if a: pass" "else: pass"est plus lent. Peut-être dépendant de la plate-forme, voyez si vous obtenez les mêmes résultats
John La Rooy
2
@cedbeu, en Python3, ils sont tous beaucoup plus rapides, mais le is Nonetest était en effet le plus lent pour moi. En pypy, ils mesuraient tous exactement la même chose :)
John La Rooy
51

Comme écrit dans PEP8 :

  • Les comparaisons avec des singletons comme None doivent toujours être effectuées avec 'is' ou 'is not', jamais avec les opérateurs d'égalité .

    Aussi, méfiez - vous d'écrire «si x» lorsque vous voulez vraiment dire «si x n'est pas None» - par exemple, lorsque vous testez si une variable ou un argument par défaut None a été défini sur une autre valeur. L'autre valeur peut avoir un type (tel qu'un conteneur) qui pourrait être faux dans un contexte booléen!

Scraplesh
la source
7
Mmmh ce n'est pas très clair, cependant, et ne répond pas à l'OP. La redirection automatique vers le PEP n'est pas toujours la bonne réponse. Très souvent, on n'est pas intéressé par le test d'identité avec le singleton None, mais juste par une vérification de la valeur de vérité. Dans ce cas, cela if A:semble plus efficace (prenez a dis.dis(), il y a les étapes supplémentaires de chargement du builtin Noneet de comparaison, avec if A is not None:, alors qu'il n'y en a qu'un jump_ifdans l'autre cas).
cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

donc ce n'est pas la même chose que

if x is not None # which works only on None
Abdul Muneer
la source
17

De nombreuses fonctions renvoient None s'il n'y a pas de résultats appropriés. Par exemple, la .first()méthode d' une requête SQLAlchemy renvoie None s'il n'y avait aucune ligne dans le résultat. Supposons que vous sélectionniez une valeur susceptible de renvoyer 0 et que vous ayez besoin de savoir si elle est réellement 0 ou si la requête n'a donné aucun résultat.

Un idiome courant est de donner à l'argument facultatif d'une fonction ou d'une méthode la valeur par défaut None, puis de tester cette valeur étant None pour voir si elle a été spécifiée. Par exemple:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

comparez cela à:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

Dans ce dernier, que se passe-t-il si vous appelez spam(0)ou spam([])? La fonction détecte (à tort) que vous n'avez pas transmis de valeur pour eggset calcule une valeur par défaut pour vous. Ce n'est probablement pas ce que vous voulez.

Ou imaginez une méthode comme "retourner la liste des transactions pour un compte donné". Si le compte n'existe pas, il peut renvoyer None. Ceci est différent de renvoyer une liste vide (ce qui signifierait que "ce compte existe mais n'a pas enregistré de transactions).

Enfin, revenons à la base de données. Il y a une grande différence entre NULL et une chaîne vide. Une chaîne vide indique généralement "il y a une valeur ici, et cette valeur n'est rien du tout". NULL indique que "cette valeur n'a pas été saisie."

Dans chacun de ces cas, vous voudriez utiliser if A is None. Vous recherchez une valeur spécifique - Aucune - pas seulement "toute valeur qui se trouve être convertie en False".

Kirk Strauser
la source
10

Ils font des choses très différentes .

Les contrôles ci - dessous si A a quelque chose à l' exception des valeurs False, [], None, ''et 0. Il vérifie la valeur de A.

if A:

Ce qui suit vérifie si A est un objet différent de None. Il vérifie et compare la référence (adresse mémoire) de A et None.

if A is not None:

MISE À JOUR: Explication supplémentaire

Souvent, les deux semblent faire la même chose, donc beaucoup de gens les utilisent de manière interchangeable - c'est une très mauvaise idée. La raison pour laquelle les deux donnent les mêmes résultats est souvent par pure coïncidence due à des optimisations de l'interpréteur / compilateur comme l' internement ou autre chose.

Avec ces optimisations à l'esprit, les entiers et les chaînes de même valeur finissent par utiliser le même espace mémoire. Cela explique probablement pourquoi deux chaînes distinctes agissent comme si elles étaient identiques.

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

Cependant, d'autres choses ne se comportent pas de la même manière.

> a = []
> b = []
> a is b
False
> a == b
True

Les deux listes ont clairement leur propre mémoire. Étonnamment, les tuples se comportent comme des chaînes.

> a = ()
> b = ()
> a is b
True
> a == b
True

C'est probablement parce que les tuples sont garantis de ne pas changer, il est donc logique de réutiliser la même mémoire.

En bout de ligne, vous ne pouvez pas vous fier aux coïncidences . Ce n'est pas parce que ça caque comme un canard que c'est un canard. Utilisez iset en ==fonction de ce que vous voulez vraiment vérifier. Ces choses peuvent être difficiles à déboguer car se islit comme de la prose que nous passons souvent en revue.

Pithikos
la source
Noneest un singleton, ce n'est pas un détail d'implémentation (contrairement à int ou string interning). Je ne suis pas sûr de comprendre ce que tu veux dire.
Lev Levitsky
@LevLevitsky mon point n'est pas que Nonese comporte différemment intou en strraison de l'internement. Mon point est que iset ==vérifie des choses différentes; vérifie d'abord une adresse mémoire, le second vérifie le contenu des adresses mémoire.
Pithikos
@LevLevitsky J'ai édité ma réponse maintenant pour la rendre un peu plus digeste.
Pithikos
7

if A: se révélera faux si A vaut 0, Faux, chaîne vide, liste vide ou Aucun, ce qui peut conduire à des résultats indésirables.

Keflavich
la source
1
Et beaucoup d'autres valeurs aussi, telles que liste vide, ensemble vide, tuple vide etc. Essentiellement, tout ce qui n'est pas véridique par docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod
6

La plupart des guides que j'ai vus suggèrent que vous devriez utiliser

si un:

sauf si vous avez une raison d'être plus précis.

Il y a quelques légères différences. Il existe des valeurs autres que None qui renvoient False, par exemple des listes vides ou 0, alors réfléchissez à ce que vous testez réellement.

Colin Coghill
la source
5

None est une valeur spéciale en Python qui désigne généralement une variable non initialisée. Pour tester si A n'a pas cette valeur particulière, utilisez:

if A is not None

Les valeurs Falsey sont une classe spéciale d'objets en Python (par exemple, false, []). Pour tester si A est faux, utilisez:

if not A

Ainsi, les deux expressions ne sont pas les mêmes et vous feriez mieux de ne pas les traiter comme des synonymes.


PS None est également faux, donc la première expression implique la seconde. Mais la seconde couvre d'autres valeurs fausses en plus de None. Maintenant ... si vous pouvez être sûr que vous ne pouvez pas avoir d'autres valeurs fausses en plus de None dans A, alors vous pouvez remplacer la première expression par la seconde.

mircealungu
la source
Ce n'est pas que vous vous trompiez, mais cette réponse couvre déjà cela.
Makoto
boh, je voudrais vous demander de supprimer le vote négatif si cela ne vous dérange pas. ma réponse est équivalente à toutes les autres, peut-être moins à celle dont vous parlez, mais elle est présentée sous un angle différent, et cela pourrait être plus facile à comprendre pour quelqu'un. de plus, quand je vois un vote négatif sur SO, je suppose une mauvaise réponse ... ce que vous admettez n'est pas le cas.
mircealungu
Eh bien, cette hypothèse est incomplète. Cela pourrait être faux ou simplement inutile. Puisque votre réponse, comme je l'ai dit, était déjà couverte, je ne suis pas convaincu qu'elle soit utile.
Makoto
4

Ça dépend du contexte.

J'utilise if A:quand je m'attends Aà être une sorte de collection, et je veux seulement exécuter le bloc si la collection n'est pas vide. Cela permet à l'appelant de passer n'importe quelle collection bien comportée, vide ou non, et de lui faire faire ce que j'attends. Il permet également Noneet Falsede supprimer l'exécution du bloc, ce qui est parfois pratique pour appeler du code.

OTOH, si je m'attends Aà être un objet complètement arbitraire mais qu'il aurait pu être défini par défaut None, alors je l' utilise toujoursif A is not None , car le code d'appel aurait pu délibérément passer une référence à une collection vide, une chaîne vide ou un type numérique de valeur 0, ou booléen False, ou une instance de classe qui se trouve être fausse dans un contexte booléen.

Et d'un autre côté, si je m'attends Aà être quelque chose de plus spécifique (par exemple, une instance d'une classe dont je vais appeler des méthodes), mais cela aurait pu être défini par défaut None, et je considère la conversion booléenne par défaut comme un propriété de la classe Cela ne me dérange pas d'appliquer à toutes les sous-classes, alors je vais simplement utiliser if A:pour sauver mes doigts le terrible fardeau de taper 12 caractères supplémentaires.

Ben
la source
3

J'ai créé un fichier appelé test.pyet l' ai exécuté sur l'interpréteur. Vous pouvez changer ce que vous voulez, pour vérifier avec certitude comment les choses se passent dans les coulisses.

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

C'est la différence de l'assembleur:

La source:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>
utilisateur
la source
2

Le premier est plus pythonique (meilleur code idéomatique), mais n'exécutera pas le bloc si A est faux (et non aucun).

Boréalide
la source
7
-1. Comme le mentionne @klesh, PEP8 dit d'utiliser is/is not None. Après PEP8 est Pythonic. D'ailleurs les deux tests sont différents .
JCotton
1

python> = 2,6,

si nous écrivons comme

if A:

générera un avertissement comme,

FutureWarning: Le comportement de cette méthode changera dans les versions futures. Utilisez plutôt le test spécifique «len (elem)» ou «elem is not None».

Nous pouvons donc utiliser

if A is not None:
normalUser
la source