Comment faire référence à l'objet null en Python?
En Python, l'objet 'null' est le singleton None
.
La meilleure façon de vérifier les choses pour "Noneness" est d'utiliser l'opérateur d'identité, is
:
if foo is None:
...
None
, null de Python?Il n'y a pas null
en Python, mais plutôt None
. Comme indiqué précédemment, le moyen le plus précis de vérifier si quelque chose a été attribué à None
consiste à utiliser l'opérateur d'identité is
, qui vérifie que deux variables se rapportent au même objet.
>>> foo is None
True
>>> foo = 'bar'
>>> foo is None
False
None
None
est l'unique instance de la classe NoneType
et toute tentative ultérieure d'instanciation de cette classe renverra le même objet, ce qui fera de None
un singleton. Les nouveaux arrivants sur Python voient souvent des messages d'erreur mentionnant NoneType
et se demandent en quoi il consiste. Je pense personnellement que ces messages pourraient simplement nommer None
car, comme nous le verrons bientôt, None
laisse peu de place à l’ambiguïté. Donc, si vous voyez un message TypeError
qui mentionne que NoneType
ne peut pas faire ceci ou ne peut pas faire cela, sachez simplement que c'est simplement le message None
qui était utilisé d'une manière qui ne le pouvait pas.
De plus, None
est une constante intégrée, dès que vous démarrez Python, vous pouvez l'utiliser de partout, que ce soit dans un module, une classe ou une fonction. NoneType
, en revanche, n’est pas le cas. Vous devez d’abord y faire référence en interrogeant None
pour sa classe.
>>> NoneType
NameError: name 'NoneType' is not defined
>>> type(None)
NoneType
Vous pouvez vérifier l'unicité de None
avec la fonction d'identité de Python id()
. Il renvoie le numéro unique attribué à un objet, chaque objet en a un. Si l'id de deux variables est identique, alors elles pointent en fait sur le même objet.
>>> NoneType = type(None)
>>> id(None)
10748000
>>> my_none = NoneType()
>>> id(my_none)
10748000
>>> another_none = NoneType()
>>> id(another_none)
10748000
>>> def function_that_does_nothing(): pass
>>> return_value = function_that_does_nothing()
>>> id(return_value)
10748000
None
ne peut pas être écraséDans une version beaucoup plus ancienne de Python (avant la version 2.4), il était possible de réaffecter None
, mais plus maintenant. Même pas en tant qu'attribut de classe ou dans les limites d'une fonction.
# In Python 2.7
>>> class SomeClass(object):
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: cannot assign to None
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to None
# In Python 3.5
>>> class SomeClass:
... def my_fnc(self):
... self.None = 'foo'
SyntaxError: invalid syntax
>>> def my_fnc():
None = 'foo'
SyntaxError: cannot assign to keyword
Il est donc prudent de supposer que toutes les références None
sont les mêmes. Il n'y a pas de "custom" None
.
None
, utilisez l'opérateur is
.Lorsque vous écrivez du code, vous pourriez être tenté de tester Noneness comme ceci:
if value==None:
pass
Ou pour tester le mensonge comme ça
if not value:
pass
Vous devez comprendre les implications et pourquoi c'est souvent une bonne idée d'être explicite.
None
pourquoi faire ceci
value is None
plutôt que
value==None
Le premier est équivalent à:
id(value)==id(None)
Alors que l'expression value==None
s'applique en fait comme ceci
value.__eq__(None)
si la valeur est vraiment None
, vous obtiendrez ce que vous espériez.
>>> nothing = function_that_does_nothing()
>>> nothing.__eq__(None)
True
Dans la plupart des cas, le résultat sera le même, mais la méthode __eq__()
ouvre une porte qui annule toute garantie d’exactitude, puisqu’elle peut être remplacée dans une classe pour fournir un comportement spécial.
Considérez cette classe.
>>> class Empty(object):
... def __eq__(self, other):
... return not other
Donc, vous essayez sur None
et cela fonctionne
>>> empty = Empty()
>>> empty==None
True
Mais alors cela fonctionne aussi sur la chaîne vide
>>> empty==''
True
Et encore
>>> ''==None
False
>>> empty is None
False
None
comme booléenLes deux tests suivants
if value:
# do something
if not value:
# do something
sont en fait évalués comme
if bool(value):
# do something
if not bool(value):
# do something
None
est un "falsey", ce qui signifie que s'il est converti en un booléen, il retournera False
et que, s'il est appliqué, l'opérateur not
, il retournera True
. Notez cependant que ce n'est pas une propriété unique à None
. En plus de False
, la propriété est partagée par des listes vides, des nuplets, des ensembles, des dictons, des chaînes, ainsi que par 0 et tous les objets des classes qui implémentent la méthode magique __bool__()
pour renvoyer False
.
>>> bool(None)
False
>>> not None
True
>>> bool([])
False
>>> not []
True
>>> class MyFalsey(object):
... def __bool__(self):
... return False
>>> f = MyFalsey()
>>> bool(f)
False
>>> not f
True
Par conséquent, lorsque vous testez des variables de la manière suivante, soyez très conscient de ce que vous incluez ou excluez du test:
def some_function(value=None):
if not value:
value = init_value()
Dans ce qui précède, vouliez-vous appeler init_value()
lorsque la valeur est spécifiquement définie sur None
ou voulez-vous dire qu'une valeur définie sur 0
, ou la chaîne vide, ou qu'une liste vide doit également déclencher l'initialisation. Comme je l'ai dit, soyez attentif. Comme souvent dans Python explicite vaut mieux qu'implicite.
None
en pratiqueNone
utilisé comme valeur de signalNone
a un statut spécial en Python. C'est une valeur de référence favorite, car de nombreux algorithmes la traitent comme une valeur exceptionnelle. Dans de tels scénarios, il peut être utilisé comme un indicateur pour signaler qu'une condition nécessite un traitement spécial (tel que la définition d'une valeur par défaut).
Vous pouvez affecter None
aux arguments de mot clé d'une fonction, puis le tester explicitement.
def my_function(value, param=None):
if param is None:
# do something outrageous!
Vous pouvez le renvoyer comme valeur par défaut lorsque vous essayez d'obtenir l'attribut d'un objet, puis le tester explicitement avant de faire quelque chose de spécial.
value = getattr(some_obj, 'some_attribute', None)
if value is None:
# do something spectacular!
Par défaut, la méthode get()
d'un dictionnaire renvoie None
lors d'une tentative d'accès à une clé non existante:
>>> some_dict = {}
>>> value = some_dict.get('foo')
>>> value is None
True
Si vous deviez y accéder en utilisant la notation en indice, KeyError
serait déclenché
>>> value = some_dict['foo']
KeyError: 'foo'
De même si vous essayez de faire apparaître un élément non existant
>>> value = some_dict.pop('foo')
KeyError: 'foo'
que vous pouvez supprimer avec une valeur par défaut généralement définie sur None
value = some_dict.pop('foo', None)
if value is None:
# booom!
None
utilisé à la fois comme indicateur et comme valeur valideLes utilisations décrites ci-dessus de None
s'appliquent lorsqu'il n'est pas considéré comme une valeur valide, mais plutôt comme un signal pour faire quelque chose de spécial. Il existe cependant des situations où il importe parfois de savoir d'où provient None
car, même s'il est utilisé comme signal, il peut également faire partie des données.
Lorsque vous interrogez un objet pour son attribut avec getattr(some_obj, 'attribute_name', None)
, récupérer None
ne vous indique pas si l'attribut auquel vous tentiez d'accéder était défini sur None
ou s'il était totalement absent de l'objet. Même situation lorsque vous accédez à une clé à partir d'un dictionnaire comme some_dict.get('some_key')
, vous ne savez pas si some_dict['some_key']
est manquant ou s'il est simplement défini sur None
. Si vous avez besoin de ces informations, la manière habituelle de les gérer consiste à tenter d'accéder directement à l'attribut ou à la clé à partir d'une construction try/except
:
try:
# equivalent to getattr() without specifying a default
# value = getattr(some_obj, 'some_attribute')
value = some_obj.some_attribute
# now you handle `None` the data here
if value is None:
# do something here because the attribute was set to None
except AttributeError:
# we're now hanling the exceptional situation from here.
# We could assign None as a default value if required.
value = None
# In addition, since we now know that some_obj doesn't have the
# attribute 'some_attribute' we could do something about that.
log_something(some_obj)
De même avec dict:
try:
value = some_dict['some_key']
if value is None:
# do something here because 'some_key' is set to None
except KeyError:
# set a default
value = None
# and do something because 'some_key' was missing
# from the dict.
log_something(some_dict)
Les deux exemples ci-dessus montrent comment gérer les cas d'objet et de dictionnaire, qu'en est-il des fonctions? Même chose, mais nous utilisons le mot-clé double astérisque à cette fin:
def my_function(**kwargs):
try:
value = kwargs['some_key']
if value is None:
# do something because 'some_key' is explicitly
# set to None
except KeyError:
# we assign the default
value = None
# and since it's not coming from the caller.
log_something('did not receive "some_key"')
None
utilisé uniquement comme valeur valideSi vous constatez que votre code est encombré du motif try/except
ci-dessus pour différencier simplement les indicateurs None
des données None
, utilisez simplement une autre valeur de test. Il existe un modèle dans lequel une valeur située en dehors du jeu de valeurs valides est insérée en tant que partie des données dans une structure de données et est utilisée pour contrôler et tester des conditions spéciales (par exemple, des limites, un état, etc.). Une telle valeur est appelée sentinel et peut être utilisée de la même manière que None
comme signal. C'est trivial de créer une sentinelle en Python.
undefined = object()
L'objet undefined
ci-dessus est unique et ne fait pas grand-chose qui puisse intéresser un programme. Il constitue donc un excellent remplacement du drapeau None
. Certaines mises en garde s'appliquent, plus à ce sujet après le code.
Avec fonction
def my_function(value, param1=undefined, param2=undefined):
if param1 is undefined:
# we know nothing was passed to it, not even None
log_something('param1 was missing')
param1 = None
if param2 is undefined:
# we got nothing here either
log_something('param2 was missing')
param2 = None
Avec dict
value = some_dict.get('some_key', undefined)
if value is None:
log_something("'some_key' was set to None")
if value is undefined:
# we know that the dict didn't have 'some_key'
log_something("'some_key' was not set at all")
value = None
Avec un objet
value = getattr(obj, 'some_attribute', undefined)
if value is None:
log_something("'obj.some_attribute' was set to None")
if value is undefined:
# we know that there's no obj.some_attribute
log_something("no 'some_attribute' set on obj")
value = None
Comme je l'ai mentionné précédemment, les sentinelles personnalisées s'accompagnent de mises en garde. Premièrement, ce ne sont pas des mots clés comme None
, donc python ne les protège pas. Vous pouvez écraser votre undefined
ci-dessus à tout moment, n’importe où dans le module défini, faites donc attention à la façon dont vous les exposez et les utilisez. Ensuite, l'instance renvoyée par object()
n'est pas un singleton. Si vous passez cet appel 10 fois, vous obtenez 10 objets différents. Enfin, l’utilisation d’une sentinelle est extrêmement idiosyncratique. Une sentinelle est spécifique à la bibliothèque dans laquelle elle est utilisée et, en tant que telle, sa portée devrait généralement être limitée aux ressources internes de la bibliothèque. Il ne devrait pas "fuir". Le code externe ne doit en prendre conscience que s'il a pour but d'étendre ou de compléter l'API de la bibliothèque.
Ce n'est pas appelé null comme dans d'autres langues, mais None
. Il n'y a toujours qu'une seule instance de cet objet, vous pouvez donc vérifier l'équivalence avec x is None
(comparaison d'identité) au lieu de x == None
, si vous le souhaitez.
En Python, pour représenter l'absence d'une valeur, vous pouvez utiliser la valeur Aucune ( types.NoneType.None ) pour les objets et "" (ou len () == 0 ) pour les chaînes. Donc:
if yourObject is None: # if yourObject == None:
...
if yourString == "": # if yourString.len() == 0:
...
En ce qui concerne la différence entre "==" et "est", le test d'identité d'objet à l'aide de "==" devrait suffire. Cependant, étant donné que l'opération "est" est définie comme l'opération d'identification d'objet, il est probablement plus correct de l'utiliser, plutôt que "==". Pas sûr s'il y a même une différence de vitesse.
Quoi qu'il en soit, vous pouvez regarder:
Per test de valeur de vérité , 'Aucun' teste directement comme FALSE, ainsi l'expression la plus simple suffira:
if not foo: