#python
Вопрос:
На мой взгляд, такие вещи, как float('nan')
следует оптимизировать, но, по-видимому, они не в Python.
>>> NaN = float('nan')
>>> a = [ 1, 2, 3, NaN ]
>>> NaN in a
True
>>> float('nan') in a
False
Имеет ли это какой-то смысл в том, чтобы не оптимизировать nan
, как другие вещи?
На мой взгляд, nan
это всего лишь nan
.
Кроме того, когда вы используете эти sorted
вещи, они дают странные результаты:
>>> sorted([3, nan, 4, 2, nan, 1])
[3, nan, 1, 2, 4, nan]
>>> 3 > float('nan')
False
>>> 3 < float('nan')
False
Сравнение nan
определяется так, но мне это не кажется «питоническим». Почему это не вызывает ошибки?
Комментарии:
1. Мне нравится это наблюдение, и я тоже думаю, что оно сбивает с толку. Какое из 7 утверждений должно вызывать исключение?
2. В C# в результате
[NaN, NaN, 1,2,3,4]
получается более питонический, ИМХО.3. Простой ответ: «потому что так определяет его IEEE754 по причинам, которые, по крайней мере, в то время, когда он был определен, имели смысл, по крайней мере, для людей, определяющих стандарт».
4. Одной из интересных особенностей NaN является то, что он нарушает протокол «(a is b) подразумевает (a==b)», и
in
оператор проверяет личность, прежде чем проверять равенство. Это означает, чтоx in somelist
это может быть правдой одновременно сall(x != y for y in somelist)
.5. Что вы подразумеваете под «оптимизацией»?
Ответ №1:
Тестирование на членство
Два разных экземпляра float('nan')
не равны друг другу. Они «Не являются числом», поэтому имеет смысл, что они также не должны быть равными. Это разные экземпляры объектов, которые не являются числами:
print(float('nan') == float('nan')) # False
Как описано здесь:
Для типов контейнеров, таких как список, кортеж, набор, набор, диктант или коллекции.дек, выражение x в y эквивалентно любому(x равно e или x == e для e в y).
Есть проверка личности! вот почему вы видите такое поведение в своем вопросе и почему NaN in a
возвращается True
и float('nan') in a
не возвращается.
Сортировка на Python
Python использует алгоритм Timsort для своей sorted()
функции. (Также смотрите это для текстового объяснения.) Я не собираюсь вдаваться в это. Я просто хочу продемонстрировать простой пример:
Это мой класс A
. Это будет нашей float('nan')
целью. Он действует так float('nan')
, как если бы он возвращался False
для всех операций сравнения:
class A:
def __init__(self, n):
self.n = n
def __lt__(self, other):
print(self, 'lt is calling', other)
return False
def __gt__(self, other):
print(self, 'gt is calling', other)
return False
def __repr__(self):
return f'A({self.n})'
class B:
def __init__(self, n):
self.n = n
def __lt__(self, other):
print(self, 'lt is calling', other)
return False
def __gt__(self, other):
print(self, 'gt is calling', other)
return False
def __repr__(self):
return f'B({self.n})'
Когда мы используем sorted()
функцию (или .sort()
метод a list
) без reverse=True
аргумента, мы просим, чтобы итерируемые объекты были отсортированы в порядке возрастания. Для этого Python пытается вызвать __lt__
метод последовательно, начиная со второго объекта в списке, чтобы увидеть, меньше ли он предыдущего объекта и так далее:
lst = [A(1), B(2), A(3), B(4)]
print(sorted(lst))
выход :
B(2) lt is calling A(1)
A(3) lt is calling B(2)
B(4) lt is calling A(3)
[A(1), B(2), A(3), B(4)]
Теперь возвращаемся к вашему примеру:
lst = [3, A(1), 4, 2, A(1), 1]
print(sorted(lst))
выход:
A(1) lt is calling 3
A(1) gt is calling 4
A(1) gt is calling 2
A(1) lt is calling 2
A(1) lt is calling 4
A(1) gt is calling 1
[3, A(1), 1, 2, 4, A(1)]
A(1).__lt__(3)
будет возвращатьFalse
. Это означаетA(1)
, что не менее 3 или это средство3
находится в правильном положении относительноA(1)
.- Затем здесь
int.__lt__(4, A(1))
вызывается и, поскольку он возвращаетNotImplemented
объект, Python проверяет,A(1)
реализован__gt__
ли он, и да, поэтомуA(1).__gt__(4)
вернетсяFalse
снова, и это означаетA(1)
, что объект находится в правильном месте относительно4
. - (И Т.д.)
Вот почему результат sorted()
кажется странным, но он предсказуем. A(1)
объект в обоих случаях, я имею в виду, когда int
класс возвращается NotImplemented
и когда __lt__
вызывается из A(1)
, вернет False.
Лучше проверить алгоритм Timsort и учесть эти моменты. Я бы включил оставшиеся шаги, если бы внимательно прочитал алгоритм Timsort.
Комментарии:
1. @don’talkjustcode Спасибо, исправлено, а также обновлено с лучшим примером.
2. Да, лучше с B. Можно также заставить B вместо этого вести себя как int, т. Е. Нормально сравнивать с другими объектами B и возвращать не реализованные объекты A. Тогда мы увидим этот
int.__lt__(4, A(1))
вызов в выходных данных