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
проверяется с помощью оператора ==
.
EDIT:
В ответ на романа, кажется, что __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 во встроенном контейнере undefined.
Это очень опасно и очень тонко. Кто-то может запустить сам код, который я показал выше, и неправильно заключить, что он безопасен для тестирования членства NaN с помощью in
.
Я не думаю, что это идеальное решение этой проблемы. Один, очень безопасный подход - обеспечить, чтобы NaN никогда не добавлялись во встроенные контейнеры. (Это боль, чтобы проверить это на всем протяжении кода...)
Другая альтернатива - следить за случаями, когда in
может иметь NaN с левой стороны, а в таких случаях тестировать членство NaN отдельно, используя math.isnan()
. Кроме того, необходимо также избегать или переписывать другие операции (например, установить пересечение).