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