web-dev-qa-db-fra.com

objet null en Python?

Comment faire référence à l'objet null en Python?

1046
Lizard

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:
    ...
1412
Ben James

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

Les bases

Il y a et il ne peut y avoir qu'un seul 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.

Pour tester 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.

Cas 1: tester si une valeur est 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

Cas 2: Utiliser None comme booléen

Les 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 pratique

None utilisé comme valeur de signal

None 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 valide

Les 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 valide

Si 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.

86
Michael Ekoka

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.

63
AndiDog

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:

29
Paolo Rovelli

Per test de valeur de vérité , 'Aucun' teste directement comme FALSE, ainsi l'expression la plus simple suffira:

if not foo:
0
artejera