NaN обрабатывается отлично, когда я проверяю его наличие в списке или наборе.Но я не понимаю как.[ОБНОВЛЕНИЕ: нет, это не так;сообщается о наличии, если обнаружен идентичный экземпляр NaN;если обнаруживаются только неидентичные экземпляры NaN, он считается отсутствующим.]
Я думал, что присутствие в списке проверяется равенством, поэтому я ожидал, что NaN не будет найден, посколькуNaN! = NaN.
hash (NaN) и hash (0) равны 0. Как словари и наборы различают NaN и 0?
Безопасно ли проверять наличие NaN в произвольном контейнере с помощью оператора in
?Или это зависит от реализации?
Мой вопрос о Python 3.2.1;но если в будущих версиях будут какие-либо изменения, существующие или запланированные, я бы тоже хотел об этом знать.
NaN = float('nan')
print(NaN != NaN) # True
print(NaN == NaN) # False
list_ = (1, 2, NaN)
print(NaN in list_) # True; works fine but how?
set_ = {1, 2, NaN}
print(NaN in set_) # True; hash(NaN) is some fixed integer, so no surprise here
print(hash(0)) # 0
print(hash(NaN)) # 0
set_ = {1, 2, 0}
print(NaN in set_) # False; works fine, but how?
Обратите внимание, что если я добавлю экземпляр пользовательского класса в list
,и затем проверяет наличие содержимого, вызывается метод экземпляра __eq__
(если он определен) - по крайней мере в CPython.Вот почему я предположил, что list
сдерживание проверяется с использованием оператора ==
.
РЕДАКТИРОВАТЬ:
В ответе Романа может показаться, что __contains__
для list
, tuple
, set
, dict
ведет себя очень странным образом:
def __contains__(self, x):
for element in self:
if x is element:
return True
if x == element:
return True
return False
Я говорю «странно», потому что я не вижу объяснения в документации (возможно, я пропустил его), и я думаю,это то, что не следует оставлять в качестве выбора реализации.
Конечно, один объект NaN может не быть идентичным (в смысле id
) другому объекту NaN.(Это неудивительно; Python не гарантирует такой идентичности. На самом деле, я никогда не видел, чтобы CPython разделял экземпляр NaN, созданный в разных местах, даже если он разделяет экземпляр с небольшим числом или короткой строкой.) Это означает, чтопроверка наличия NaN во встроенном контейнере не определена.
Это очень опасно и очень неуловимо.Кто-то может запустить тот код, который я показал выше, и ошибочно заключить, что безопасно проверять членство в NaN, используя in
.
Я не думаю, что есть идеальный обходной путь для этой проблемы.Один, очень безопасный подход, состоит в том, чтобы гарантировать, что NaN никогда не добавляются во встроенные контейнеры.(Это сложно проверить по всему коду ...)
Другая альтернатива - следить за случаями, когда in
может иметь NaN с левой стороны, и в таких случаях проверять членство в NaNотдельно, используя math.isnan()
.Кроме того, необходимо избегать или переписывать другие операции (например, устанавливать пересечение).