Как стать автором
Обновить

Комментарии 16

Не знаю питон, но сомневаюсь, что там для проверки числа нужно генерировать массив на миллион элементов. Почему больше-меньше не используется?

Тут в целом больше вопросов, чем ответов. Называем статью "Эффективное использование any и all в Python" и говорим, что выделить память на весь список дороже, чем создать генератор. Ну такое

Интересно, в WunderFund production-код так же пишут?

Согласен, авторы просто усложняют жизнь, потому что больше-меньше работает вполне достойно:

ch = 200000000
if ch >= 1 and ch <= 1000000000:
    print(True)
else:
    print(False)

При этом, код выполняется менее, чем за 1 секунду...

На Питоне это записывается проще:

print(1 <= ch <= 1000000000)

Возможно, автор имел в виду какую-то более сложную задачу, для которой действительно нужна функция any или all.

Или можно так:

print(ch in range(1, 1000000001))

Почему больше-меньше не используется?

Возможно, это "игрушечный пример" просто. Типа, у нас есть большой пребольшой массив, и в нем надо что-то искать. И автор не стал заморачиваться, просто сгенерировал его из чисел от 1 до много.

Так вся проблема у него именно из-за генерации. В случае наличия самого массива и проблемы с генерацией нет.

Не знаю Питона, но вообще эти списковые включения выглядят весьма интересно.

В каких языках они еще есть?

Является ли само по себе выражение "спискового включения" объектом, или оно сразу "раскрывается" в список (запускается на генерацию)? (здесь я имею в виду аналогию с лямбда-функцией в большинстве языков: само по себе лямбда-выражение - это не вызов функции, а специальный объект, который можно сохранить в переменной; вызов функции происходит только при применении операции вызова "()" )

Еще приходит в голову аналогия с диапазонными объектами, которые тоже есть в некоторых языках. Выражения вида "10..20" можно интерпретировать как специальные объекты, хранящие два числа, а можно - как полноценные списки всех чисел в заданном диапазоне.

Не создавайте списки для однопроходных алгоритмов, для этого есть генераторы

Весь смысл статьи. Это ещё и перевод. Не, ну понятно, корпоративный блог кушает деньги, нужно в него что-то писать.

Всегда опасаюсь, что кто-то через поиск найдет по тексту задачи подобную информацию и перенесет в проект решение не глядя...
Про операции сравнения уже написали выше в комментариях, поэтому вот еще один способ решения задачи, более близкий к исходному варианту + сравнение с функцией из статьи:

def foo() -> bool:
    return 200_000_000 in (range(1_000_000_000))

from dis import dis
dis(foo)
  2           0 LOAD_CONST               1 (200000000)
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_CONST               2 (1000000000)
              6 CALL_FUNCTION            1
              8 COMPARE_OP               6 (in)
             10 RETURN_VALUE
			 
			 
def find_200_million() -> bool:
    return any(True for number in range(1_000_000_000) if number == 200_000_000)

dis(find_200_million)
  2           0 LOAD_GLOBAL              0 (any)
              2 LOAD_CONST               1 (<code object <genexpr> at 0x7f3d31d61930, file "<stdin>", line 2>)
              4 LOAD_CONST               2 ('find_200_million.<locals>.<genexpr>')
              6 MAKE_FUNCTION            0
              8 LOAD_GLOBAL              1 (range)
             10 LOAD_CONST               3 (1000000000)
             12 CALL_FUNCTION            1
             14 GET_ITER
             16 CALL_FUNCTION            1
             18 CALL_FUNCTION            1
             20 RETURN_VALUE

Позволю себе развернуть мысль: если нужно проверить, входит ли элемент в последовательность, — просто используйте синтаксис


x in y

Интерпретатор сам все сделает наилучшим образом: для перечисления или словаря достанет значение по ключу, для списка линейно переберет элементы до первого совпадения и вернет True.
Проверка на вхождение числа в диапазон (или, в общем случае, арифметическую прогрессию), представленный объектом range, выполняется за единицы микросекунд и не зависит от величины числа или диапазона.


>>> from timeit import timeit
>>> timeit('200_000_000 in range(1_000_000_000)', number=1)
2.7045607566833496e-06

Показатели неполные! Для корректности нужно смотреть также и на наихудший результат, т.к. для поиска последнего элемента в каком то варианте кода может понадобиться неожиданно больше времени чем в другом варианте кода. Я решил перепроверить.

Ищем значение 200000000
any([number == 200000000 for number in range(1_000_000_000)])
0:00:45.137719
any([True for number in range(1_000_000_000) if number == 200000000])
0:00:35.904143
any(True for number in range(1_000_000_000) if number == 200000000)
0:00:07.327660
any(number == 200000000 for number in range(1_000_000_000))
0:00:10.658496
all(False for number in range(1_000_000_000) if number == 200000000)
0:00:07.024172

Далее решил проверить поиск числа 1.

Ищем значение 1
any([number == 1 for number in range(1_000_000_000)])
0:00:44.296145
any([True for number in range(1_000_000_000) if number == 1])
0:00:37.544449
any(True for number in range(1_000_000_000) if number == 1)
0:00:00.000023
any(number == 1 for number in range(1_000_000_000))
0:00:00.000003
all(False for number in range(1_000_000_000) if number == 1)
0:00:00.000003

Ну и конечно же худший вариант, число 999999999.

Ищем значение 999999999
any([number == 999999999 for number in range(1_000_000_000)])
0:00:50.238195
any([True for number in range(1_000_000_000) if number == 999999999])
0:00:37.451774
any(True for number in range(1_000_000_000) if number == 999999999)
0:00:36.485738
any(number == 999999999 for number in range(1_000_000_000))
0:00:49.075029
all(False for number in range(1_000_000_000) if number == 999999999)
0:00:36.864547

Как видно, результат не такой уж и радостный, но в некоторых случаях лучше чем изначальный вариант :)

Генераторы не требуют памяти, но делают больше вычислений.
Создание списка меньше действий, но требует выделения памяти.

Разное железо, версия питона могут немного поменять позиции в списке для худшего случая.

В первом слове заголовка потерялась приставка 'не'.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий