web-dev-qa-db-fra.com

dans l'opérateur, float ("NaN") et np.nan

J'avais l'habitude de croire que l'opérateur in dans Python vérifie la présence de l'élément dans une collection en utilisant la vérification d'égalité ==, Donc element in some_list Est à peu près équivalent à any(x == element for x in some_list). Par exemple:

True in [1, 2, 3]
# True because True == 1

ou

1 in [1., 2., 3.]
# also True because 1 == 1.

Cependant, il est bien connu que NaN n'est pas égal à lui-même. Je m'attendais donc à ce que float("NaN") in [float("NaN")] soit False. Et c'est False en effet.

Cependant, si nous utilisons numpy.nan Au lieu de float("NaN"), la situation est assez différente:

import numpy as np
np.nan in [np.nan, 1, 2]
# True

Mais np.nan == np.nan Donne toujours False!

Comment est-ce possible? Quelle est la différence entre np.nan Et float("NaN")? Comment in gère-t-il np.nan?

27
Ilya V. Schurov

Pour vérifier si l'élément est dans la liste, Python teste d'abord l'identité de l'objet , puis teste uniquement l'égalité si les objets sont différents.1

float("NaN") in [float("NaN")] est False car deux objets différents NaN sont impliqués dans la comparaison. Le test d'identité renvoie donc False, puis le test d'égalité renvoie également False puisque NaN != NaN.

np.nan in [np.nan, 1, 2] est cependant vrai car le même NaN objet est impliqué dans la comparaison. Le test d'identité d'objet renvoie True et ainsi Python reconnaît immédiatement l'élément comme étant dans la liste.

Le __contains__ la méthode (invoquée à l'aide de in) pour de nombreux autres types de conteneurs intégrés de Python, tels que les tuples et les ensembles, est implémentée en utilisant la même vérification.


1 Au moins, cela est vrai dans CPython. L'identité d'objet signifie ici que les objets se trouvent à la même adresse mémoire: la contient la méthode pour les listes est effectuée en utilisant PyObject_RichCompareBool qui compare rapidement les pointeurs d'objets avant une comparaison d'objet potentiellement plus compliquée. Les autres implémentations Python peuvent différer.

29
Alex Riley

Une chose à noter est que les tableaux numpy se comportent comme prévu:

a = np.array((np.nan,))
a[0] in a
# False

Variations du thème:

[np.nan]==[np.nan]
# True
[float('nan')]==[float('nan')]
# False
{np.nan: 0}[np.nan]
# 0
{float('nan'): 0}[float('nan')]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# KeyError: nan

Tout le reste est couvert dans l'excellente réponse de @ AlexRiley.

5
Paul Panzer