В книге «питон.Чистый код» посвящена была эта тема, а функции zip() в первые увидел в книге «Однострочники Питон» Очень маленькие детали!Очень рад, что автор показывает их Было бы еще интереснее, если ты показал как итераторы работают, создав свой класс(протокол итераторов)
первая задача res = n * (n + 1) // 2 я понимаю что не тема ролика, но молодому программисту обязательно нужно повторять что программист это в первую очередь умный человек который оптимизирует свои алгоритмы поиска решения.
А ведь до этого можно и случайно додуматься, к примеру когда в голову придёт мысль "Хм, ну даже в Microsoft Exel есть SUM. И в паскале на уроках информатики в школе что-то такое вроде тоже было, хоть и в сраку паскаль ибо слишком сложно. А вдруг и здесь тоже получится?")) (Это моя реальная история из жизни, пхахах))
Я знаю о функциях prod и sum, но практически никогда их не использую, так как в большинстве случаев оказывается что в формуле будет не просто сумма и все равно придется писать цикл.
Не используйте таймер как замер скорости, во 1х это показывает что разницу во времени только на вашей конфигурации, во 2х даже если вы запустите 2 раза или более значения будут отличаться, так как слишком много от чего будет зависеть время выполнения(например в данный момент при замере первого алгоритма у вас был простой а при замере второго алгоритма ос полезла проверять обновления или ещё че и все уже не достоверное отличие). Лучше поищите информацию как замерить сложность алгоритма в Пайтон.
Суть не в том, за какое время выполнится код, суть в том, чтобы увидеть разницу между 2 подходами. Он написал и запустил оба варианта одновременно, а это значит, что замер покажет разницу правильно. Ну, конечно, если вдруг компьютер не запустит все обновления и не начнёт искать вирусы ровно в тот момент, когда ты запускаешь код, получается ровно через 0.7 секунды))
Время затрачиваемое на работу зависит от кучи переменных, тестить нужно не 1 раз, а запускать код раз 100 и считать среднюю, а если требуется сложная логика, то генераторами с ума сойдёшь её программировать.
То есть, вкратце, пользуйтесь sum для суммирования списка, индексы берите из enumеrate(верно только на чтение), листы склеивайте параллельно zip-ом и по возможности фильтруйте данные в временный список перед работой с ними. Так-то все логично, но в учебниках не особо говорят о том что for по индексам медленный.
обычно когда доходит до оптимизации кода, проблема не в том, что цикл работает в 1.5-2 раза медленнее, а в том какими структурами данных ты пользуешься. Но с точки зрения написания кода, да примеры из видео как минимум читать приятнее, потому что Pythonic way
На 4:25 сначала один байт код enumirate, а в следующем кадре уже сравнивается с другим байткодом. При этом если сравнить первоначально показанный байткод, то для enumirate это 5 инструкций, а для for это всего 3, если считать от FOR_ITER до JUMP_BACKWARD. Интересно было бы сравнить enumirate с простым циклом for num in numbers с инкремент внутри переменной счётчика i объявленной вне цикла.
генераторы будут лучше по времени если ожидается получения негативного условия в выражении формирования. Здесь же в примере все значения участвуют, пример в пользу списочного выражения. Но разница все равно копеечная, экономия в памяти все равно выводит выражения генераторы на первое место.
Спасибо за видео, хоть и знал про эти методы, но лишним не будет. Про zip вообще уже забыл, когда изучал python пару раз использовал, с тех пор вообще о нем забыл. И кстати в следующий раз декоратор для замера времени используй, чтобы меньше кода было, просто не все сразу поймут просматривая это. А так все чётко.
Какой хороший питон, Я щас с# изучаю, и в начале не мог привыкнуть к этим ;, {}, строгой типизации, void, множество типов данных. Но про питон тоже не забываю.
@@nakidai потому что не привык задавать типы, к тому же каждый раз писать void, если метод ничего не возвращает. Я привык в питоне к примеру написал функцию которая просто выполняет какую-то задачу, но ничего не возвращает. Думаю, это из-за привычек, и void по началу Я забывал писать чаще, чем те же ;
@@ismailisabekov8424 Изучал С++ школьником, почти 20 лет назад уже. Сейчас всё делаю на питоне. Блин, как же это красиво: строгая типизация у всего в том числе у функий, приватные и публичные члены классов. Мой внутренний перфекционист часто негодует, что этого нет в питоне. Особенно бесит, что содержимое классов можно менять извне.
Дружище. Насчёт второго примера. 1) Повторил код твоих функций из примера пуля в пулю. У меня 100% быстрее работает с индексами и для меня это не откровение - enumerate очень долгая штука (по крайней мере до 3.10 питона включительно) - я это не однократно проверял, могу куда угодно пруфы скинуть. 2) говорить что байткода выполняется меньше, значит и код выполняется быстрее - неверно. Меньше кода это не всегда быстрее. Как раз-таки оптимизация чаще всего и приводит к увеличению кода - типа в рекурсивную функцию для вычисления чисел фиббоначи добавить кеширование вычисленных чисел - кода становится больше, а скорость вычисления улетает в космос. Больше действий не значит быстрее. Зависит от скорости каждого действия в обоих множествах действий. Ещё раз говорю - куда угодно могу скинуть пруфы что с индексами быстрее работает. И да, байткода мы там так и не увидели. Представленное является дизассемблированным кодом питона, это не байт-код, это набор ассемблерных инструкций
1-ый пример совершенно притянутый за уши, я такого в реальной жизни никогда не видел. Давайте возьмем какую-нибудь вложенную структуру, в ней что-то найдём (причём найдём как-то не так чтобы очень просто, с переходом в другие процедуры) и оттуда уже суммируем данные по циклу. Впрочем, 4-й пример именно об этом. 2-ой, да согласен, это так и есть. 3-й, согласен, zip -- сила. ))) 4-й, тоже согласен. Разница как бы не такая большая, если процедура выполняется один раз, но если оно балалайкой повторяется миллион раз в цикле... )))
Спасибо большое за видео🙂 Давай побольше такого рода сравнений, а то тема, что python медленный очень сильно засела в башке у людей моего окружения. Вот хотя бы такими видео буду потихоньку развивать у них сомнения
Если они пишут на плюсах или расте , мало что у тебя получится, хотя есть синтетические примеры где питон обгонят плюса. Правда там обычно используются написанные отцами на тех плюсах библиотеки питона против банального авторскогобыдлокода на плюсах. Прелесть питона не в скорости, и там где она реально нужна на нем писать никогда не будут, будут на расте. Питон нужен для скорости/дешевизны разработки и поддержки
@@mikeofs1304 проблема в том, что большинство людей используют питон пяткой правой ноги. Пример из жизни. Либа для аналитики. Питон. Работа с таблицами. Вместо сортировки pandas, которая оптимизирована для этих целей -- пузырек. Нашел случайно, когда на 10+млн строках ноут уже помирал... Как по мне -- такое надо показывать. Т.к. те, кто не знаком с питоном будут решать задачу стандартными алгоритмами, когда есть готовые либы "написанные отцами на тех плюсах" или те же sum(list). Пустяк, но целому отделу аналитики, работающему на тб данных работу ускорит
"Сейчас я вам покажу, как не надо использовать циклы for". В первых двух примерах создает списки через list comprehension, вместо обычного list(range(1000000)), на который тратится еще меньше времени. Более того, если продолжить, то первый пример и вовсе абсурден, ведь функции sum() не нужен список, ей нужен итерируемый объект. Так что все заканчивается как: sum(range(1000000)). И тратится на это в 3 раза меньше времени, чем на финальный вариант автора.
У меня есть 2 функции для деления текста на страницы. обрезание страницы было реализовано через цикл фор как раз по индексам. Заменил рейндж лен на енумерейт время выполнения функции возросло в 2 раза
Мне стало непонятно, почему в первом примере вместо numbers = [num for num in range(1_000_000)] не использовать просто numbers = range(1_000_000) и потом точно так же вывести sum(numbers)? Интересно было бы узнать причину.
Еще можно сократить код немного и вместо `numbers = [num for num in range(1_000_000)]` писать `numbers = range(1_000_000)` Но в первом случае тип объекта list, а во втором range, если нужен list, то просто надо написать так `numbers = list(range(1_000_000))` Не знаю как это с точки зрения производительности, но по клавишам вы нажимаете меньше, спасибо за внимание)
Уважаемые комментаторы, которые считают, что нынешних мощностей хватает, чтобы любая программа летала, а особенно ваши калькуляторы. Объясняю, во-первых все подобные замедления суммируются, то есть 0.4 секунды в одном месте, 0.8 в другом, ещё 0.6 в третьем и вот, казалось бы, простая программка из трёх функций, а две лишние секунды потрачены из-за невежества автора кода. Во-вторых, лично мне подобные лайфхаки помогли с нейронными сетями, когда каждая миллисекунда на счету и по сути вручную замеряешь каждый элементы тренировки с целью оптимизации, такие значительные изменения убирают в сумме дни обучения модели
А просто потому что, numpy написан на С, и некоторые свои функции преобразовывает под тот же С, когда свой код остаётся на питоне. P.s. Для тех, кто в танке, язык "С" работает в разы быстрее, чем питон.
@@z.dekuch1987 язык "С" не работает "в разы быстрее питона" - скомпилированные программы писанные на С работают быстрее чем скрипты интерпретируемые питоном, и не в разы а на порядки быстрее 😉
Numpy + Numba тогда. И можно оставаться на пайтоне. Обе библиотеки разрабатывались для научных вычислений, как я понял. Намба особенно впечатляет, код не усложняет и изучать не нужно. из "тонкостей" только использование кэша.
Последний пример можно реализовать через фильтр по 0 элементу списка по далее вытащить 1 элементы и потом сделать суммирование итога. Получится сильно быстрее. Вообще, for, while нужно использовать только тогда, когда идёт сложная трансформация данных, для всего остального есть генераторы, встроенные методы переборки вроде map и collections
Генератор списка и генераторное выражение не отличаются по скорости, отличие в потребление памяти, генератор списка, что очевидно создает список со всеми значениями, а генераторное выражение отдает по одному значению(имеет свойства объекта итеретора). А еще в качестве примера можно было попробовать while циклы и рекурсии.
Лист-комп - это генератор обернутый в класс лист с помощью [ ] Лист-комп не может быть быстрее генератора, ибо лист-комп = отработавший до конца генератор + создание объекта класса лист с результатами данных полученных от генератора В данном случае листкомп быстрее ибо он сравнивается не с генератором, а с генератором обернутым в ( ) То есть по сути тут сравнивается list comprehension и tuple comprehension
Эээ, нет никакого tuple comprehension в природе. Если только явно обернуть в вызов tuple генератор, тогда да, но тут же нет такого, тут обычные генераторные выражения.
После низкоуровневых языков программирования меня триггерит то, что использовать готовое решение с точки зрения оптимизации выгоднее, чем писать самому
Привет. Можно спросить не по теме видео?) Я вот пытаюсь парсить крипто сайты, но содержимое формирует JS код. Подскажи куда двигаться, чтобы максимально быстро захватить результат. Пробовал requests-html использовать, но после рендера результат тот же (без подгружаемого контента).
Через пол года попросите чатгпт ускорить ваш код и он сам все оптимизирует. Пока только в лимит токенов упирается. А что будет если скомпилировать код? Будут ли вообще различия в скорости?
Я , конечно, не программист, но если надо обрабатывать миллион и более записей, то лучше использовать какую-нибудь sql базу данных, а там уже своя встроенная оптимизация запросов.
Есть ощущение, что вся разница вовсе не от записи. При запуске, вы не можете гарантировать, что код получил процессорное время в момент старта, и на всё время выполнения, без пауз. Вспомните про gil, и как ос раздает время процессора. Итого. На 10⁶+ запусков цикла можно посмотреть среднее. Чтобы понять, есть ли смысл в этой оптимизации. И уже тогда думать, как городить тесты "в вакууме", для корректного сравнения.
for key in dict работает быстрее чем for key in dict.keys(). Как я понял это потому что первое выражение использует кэшированные данные, когда как метод keys() возвращает генератор.
зачем использовать enumerate, когда нужны одни значения? обычный итератор по значениям будет быстрее: for v in numbers: temp += v ну а лучше вообще так: for v in range(1_000_000): temp += v а еще лучше так: sum(range(1_000_000)) в последнем примере быстрее всего будет вообще так (кстати, используется генератор): print(sum(value for action, value in user_buy if action == "confirmed")) и это быстрее, чем использование списка (я уж молчу про использование памяти): print(sum([value for action, value in user_buy if action == "confirmed"]))
А в зависимости от железа один и тот же код, например на разных процессорах может выполняться разное время? Например процессоры с разными наборами инструкции?
Странно, я повторил первый пример и на проверке значения range в 100 миллионов, у меня выходит: Цикл: 8.467104000039399 сек, sum: 8.498224299983121 сек То есть, цикл оказывается быстрее. Но если указать значение в 200 миллионов, то картина меняется на противоположную и sum оказывается быстрее: Цикл: 17.2555661998922 sum: 16.959907999960706 Теперь sum побеждает. Проверял множество раз, соотношение и тенденция остаётся таким же upd: проверил на сотнях повторений и усреднении через timeit, цикл почти всегда побеждает по скорости. Хотя я прям в точности повторил и перепроверил. Да и тут сложно ошибиться... Очень странно
Есть одна проблема, когда без индекса не обойтись - когда вы планируете изменить значения в списке. И enumerate и проход по элементам создают копию, с которой вы работаете, сам элемент не меняется
Используйте встроенные функции, а в коде [num for num in range(1000)] вместо list(range(1000)), причём это заполнение ещё и в замер попадает. И вообще, сумма рейнджа решается формулой.
разница в том что мат либы написаны на С++ и скомпилированы! еще они заранее оптимизированы! так устроен python. даже ели вы тоже напишите на С++ то либа python скорее всего будет все ровно быстрее. еще не затронули тему памяти! разные подход по разному используют память. и это реально важно. хотите очень быстрый цикл пишите его на next. и у вас будет реально очень быстрое решение. писать циклом или использовать либу, зависит строго от задачи! просто сказать тип - "либа круче!" это уровень джуна.. нет понятие "так лучше" или "так хуже!", бывает задача и ее решение.)) вроде тут оптимизирует но там дальше из-за этого подхода может все остановиться и задержка вырастит или наоборот упадет. память выжрет к примеру и ПО подвиснет, зато быстро.))))
Во втором примере получил результат, противоположній результату автора: 9999999 time: 1.33 9999999 time: 1.49 Возможно, у меня более поздняя версия Python, в которой доступ по индексу оптимизирован?
Есть классная книжка по такого рода приколам питона, называется Python Cookbook, автор Дэвид Бизли. Там прям рецепты решения всяких задач описаны. Один из советов, изучить встроенную либу collections,
Проверил 2 пример из видео - доступ к элементам списка по индексу. По производительности enumerate самый не производительный способ. Python 3.11.1 Рейтинг получился такой: 1) "for num in numbers:" 2) "for num in range(len(numbers)):" 3) "for index, num in enumerate(numbers):"
Ну потому что два вида объектов так перебирается - и индекс и объекты списка. Но если обе эти сущности используются внутри цикла, то enumerate просто удобен и понятен.
Итерироваться через for, но увеличивать индекс, заданный вне цикла? Полагаю, по производительности сопоставимо с enumerate, но выглядеть это будет очень грязно.
Посыл у автора верный - имея возможность стоит всегда делать выбор в пользу коробочных функций, так как их уже оптимизировали до нас + проверять постулаты на достоверность. Но сама подача материала просто пиздец. Делаем тесты на ОДНОМ замере Карл... А еще история с "байт кодом" позабавила. Возможно, автор понимает что там написано (ощущение что нет), но делать выводы исходя только из количества инструкций это конечно топ. Так сказать разбор от бога.
Затраты памяти ещё сравните у функций с генератором и с листом в 4 примере
хорошая идея, в следующих видео добавлю и затраты ресурсов для наглядности
@@zproger и сразу станет видно, что не в скорости прелесть генератора :-)
@@splinter928 в этом конкретном случае не станет
@@dmitriyneledva4693 Из-за того что немного памяти занимает эти данные?
Пишите на ассемблере
В книге «питон.Чистый код» посвящена была эта тема, а функции zip() в первые увидел в книге «Однострочники Питон»
Очень маленькие детали!Очень рад, что автор показывает их
Было бы еще интереснее, если ты показал как итераторы работают, создав свой класс(протокол итераторов)
первая задача res = n * (n + 1) // 2 я понимаю что не тема ролика, но молодому программисту обязательно нужно повторять что программист это в первую очередь умный человек который оптимизирует свои алгоритмы поиска решения.
Здравствуйте, очень полезное видео, спасибо вам за добро которые вы делаете когда делитесь с знаниями
Благодарю за комментарий! Рад что видео принесло вам пользу!
На мой взгляд - если чел использует цикл 'For' вместо 'Sum' то это просто незнание встроенных команд / функций и т.д.
А ведь до этого можно и случайно додуматься, к примеру когда в голову придёт мысль "Хм, ну даже в Microsoft Exel есть SUM. И в паскале на уроках информатики в школе что-то такое вроде тоже было, хоть и в сраку паскаль ибо слишком сложно. А вдруг и здесь тоже получится?"))
(Это моя реальная история из жизни, пхахах))
Я знаю о функциях prod и sum, но практически никогда их не использую, так как в большинстве случаев оказывается что в формуле будет не просто сумма и все равно придется писать цикл.
Да нет ,это дает разнообразие решения задач
@@13-th_Lord паскаль слишком сложно? нууууу... вроде самый простой и логичный язык. синтаксис у него конечно так себе...
Лучший! Спасибо за качественный контент
Спасибо
на первой минуте звучит фраза, что sum - принимает итератор, что абсолютно не верно. sum - принимает iterable, т.е итерируемый объект.
Спасибо за исправление, оговорился
Не используйте таймер как замер скорости, во 1х это показывает что разницу во времени только на вашей конфигурации, во 2х даже если вы запустите 2 раза или более значения будут отличаться, так как слишком много от чего будет зависеть время выполнения(например в данный момент при замере первого алгоритма у вас был простой а при замере второго алгоритма ос полезла проверять обновления или ещё че и все уже не достоверное отличие). Лучше поищите информацию как замерить сложность алгоритма в Пайтон.
Ну или надо делать пару тысяч замеров по одной и той же задаче и выводить среднее. Тогда будет более-менее близко к правде
Надо использовать модуль timeit, он запускает фрагмент кода n раз (1000000 по умолчанию)
Суть не в том, за какое время выполнится код, суть в том, чтобы увидеть разницу между 2 подходами. Он написал и запустил оба варианта одновременно, а это значит, что замер покажет разницу правильно. Ну, конечно, если вдруг компьютер не запустит все обновления и не начнёт искать вирусы ровно в тот момент, когда ты запускаешь код, получается ровно через 0.7 секунды))
@@beardedman721замер правильно разницу не покажет, но статически значимые отличия гипотез получим именно в этих случаях…
Спасибо. Я вот уже раз 10 запустил первый пример, и у меня 0.1 время первого цикла, 0.09 - второго.
превью это жиза просто. с каждым днём всё лучше, удачи тебе!)
Благодарю, стараюсь 😉
Шишбик,согл)
@@burgershot777 :3
Отличная подача, подписался на канал!
Благодарю
А почему бы не использовать filter + map + sum вместо list comprehension + sum и generator + sum?
Время затрачиваемое на работу зависит от кучи переменных, тестить нужно не 1 раз, а запускать код раз 100 и считать среднюю, а если требуется сложная логика, то генераторами с ума сойдёшь её программировать.
В первом примере можно сократить до print(sum(range(1_000_000)))
Есть такое, об этом писал в телеграмм канале после публикации видео
То есть, вкратце, пользуйтесь sum для суммирования списка, индексы берите из enumеrate(верно только на чтение), листы склеивайте параллельно zip-ом и по возможности фильтруйте данные в временный список перед работой с ними.
Так-то все логично, но в учебниках не особо говорят о том что for по индексам медленный.
обычно когда доходит до оптимизации кода, проблема не в том, что цикл работает в 1.5-2 раза медленнее, а в том какими структурами данных ты пользуешься. Но с точки зрения написания кода, да примеры из видео как минимум читать приятнее, потому что Pythonic way
Благодарю! 🔥👍
Рад что понравилось =))
На 4:25 сначала один байт код enumirate, а в следующем кадре уже сравнивается с другим байткодом. При этом если сравнить первоначально показанный байткод, то для enumirate это 5 инструкций, а для for это всего 3, если считать от FOR_ITER до JUMP_BACKWARD. Интересно было бы сравнить enumirate с простым циклом for num in numbers с инкремент внутри переменной счётчика i объявленной вне цикла.
генераторы будут лучше по времени если ожидается получения негативного условия в выражении формирования. Здесь же в примере все значения участвуют, пример в пользу списочного выражения. Но разница все равно копеечная, экономия в памяти все равно выводит выражения генераторы на первое место.
Спасибо за видео, хоть и знал про эти методы, но лишним не будет. Про zip вообще уже забыл, когда изучал python пару раз использовал, с тех пор вообще о нем забыл. И кстати в следующий раз декоратор для замера времени используй, чтобы меньше кода было, просто не все сразу поймут просматривая это. А так все чётко.
Какой хороший питон, Я щас с# изучаю, и в начале не мог привыкнуть к этим ;, {}, строгой типизации, void, множество типов данных. Но про питон тоже не забываю.
@@ismailisabekov8424 А почему void прям отдельно? 🤔
@@nakidai потому что не привык задавать типы, к тому же каждый раз писать void, если метод ничего не возвращает. Я привык в питоне к примеру написал функцию которая просто выполняет какую-то задачу, но ничего не возвращает. Думаю, это из-за привычек, и void по началу Я забывал писать чаще, чем те же ;
@@ismailisabekov8424 Изучал С++ школьником, почти 20 лет назад уже. Сейчас всё делаю на питоне. Блин, как же это красиво: строгая типизация у всего в том числе у функий, приватные и публичные члены классов. Мой внутренний перфекционист часто негодует, что этого нет в питоне. Особенно бесит, что содержимое классов можно менять извне.
Дружище. Насчёт второго примера.
1) Повторил код твоих функций из примера пуля в пулю. У меня 100% быстрее работает с индексами и для меня это не откровение - enumerate очень долгая штука (по крайней мере до 3.10 питона включительно) - я это не однократно проверял, могу куда угодно пруфы скинуть.
2) говорить что байткода выполняется меньше, значит и код выполняется быстрее - неверно. Меньше кода это не всегда быстрее. Как раз-таки оптимизация чаще всего и приводит к увеличению кода - типа в рекурсивную функцию для вычисления чисел фиббоначи добавить кеширование вычисленных чисел - кода становится больше, а скорость вычисления улетает в космос. Больше действий не значит быстрее. Зависит от скорости каждого действия в обоих множествах действий. Ещё раз говорю - куда угодно могу скинуть пруфы что с индексами быстрее работает.
И да, байткода мы там так и не увидели. Представленное является дизассемблированным кодом питона, это не байт-код, это набор ассемблерных инструкций
Подскажи, что у тебя за дистрибутив и какое графическое окружение стоит ?
Zorin OS + дефолт окружение
1-ый пример совершенно притянутый за уши, я такого в реальной жизни никогда не видел. Давайте возьмем какую-нибудь вложенную структуру, в ней что-то найдём (причём найдём как-то не так чтобы очень просто, с переходом в другие процедуры) и оттуда уже суммируем данные по циклу. Впрочем, 4-й пример именно об этом.
2-ой, да согласен, это так и есть.
3-й, согласен, zip -- сила. )))
4-й, тоже согласен.
Разница как бы не такая большая, если процедура выполняется один раз, но если оно балалайкой повторяется миллион раз в цикле... )))
Спасибо большое за видео🙂 Давай побольше такого рода сравнений, а то тема, что python медленный очень сильно засела в башке у людей моего окружения. Вот хотя бы такими видео буду потихоньку развивать у них сомнения
.да и еще, как с использованием enumirate найти в списке максимальное кол-во повторяющихся элементов?
@@mak32 Лучше используйте collections.Counter для этого
Спасибо
Если они пишут на плюсах или расте , мало что у тебя получится, хотя есть синтетические примеры где питон обгонят плюса. Правда там обычно используются написанные отцами на тех плюсах библиотеки питона против банального авторскогобыдлокода на плюсах. Прелесть питона не в скорости, и там где она реально нужна на нем писать никогда не будут, будут на расте. Питон нужен для скорости/дешевизны разработки и поддержки
@@mikeofs1304 проблема в том, что большинство людей используют питон пяткой правой ноги.
Пример из жизни. Либа для аналитики. Питон. Работа с таблицами. Вместо сортировки pandas, которая оптимизирована для этих целей -- пузырек. Нашел случайно, когда на 10+млн строках ноут уже помирал... Как по мне -- такое надо показывать. Т.к. те, кто не знаком с питоном будут решать задачу стандартными алгоритмами, когда есть готовые либы "написанные отцами на тех плюсах" или те же sum(list). Пустяк, но целому отделу аналитики, работающему на тб данных работу ускорит
Спасибо. Ценный контент.
"Сейчас я вам покажу, как не надо использовать циклы for".
В первых двух примерах создает списки через list comprehension, вместо обычного list(range(1000000)), на который тратится еще меньше времени.
Более того, если продолжить, то первый пример и вовсе абсурден, ведь функции sum() не нужен список, ей нужен итерируемый объект.
Так что все заканчивается как: sum(range(1000000)). И тратится на это в 3 раза меньше времени, чем на финальный вариант автора.
Крсава! Все четко! Хорошо я так и писал)
Благодарю
А что быстрее работает: списки или кортежи? По кортежам тоже можно сумму находить.
У меня есть 2 функции для деления текста на страницы.
обрезание страницы было реализовано через цикл фор как раз по индексам.
Заменил рейндж лен на енумерейт время выполнения функции возросло в 2 раза
Мне стало непонятно, почему в первом примере вместо numbers = [num for num in range(1_000_000)] не использовать просто
numbers = range(1_000_000) и потом точно так же вывести sum(numbers)? Интересно было бы узнать причину.
Спасибо большое за информацию я только только начинаю спасибо
Пожалуйста, желаю успешного изучения!
Вот 2 пример - это про меня 😎
😎 😎
Шок контент - этот 🍀клевер приносит удачу, по слухам, если его скопировать и вставить в код, он сделает код оптимизированным и быстрым.
Это база!
Больше всего обидно, что реьята, которые сейчас монут учиться не учатся, а сразу бегут в разработку
практика самое лучше обучение :)
Интересно, а паттерн мэтч с вычислимыми полями работает быстрее условных операторов?..
Доброго времени суток. Какую программу используюте для написания кода?
Vs Code
@@zproger благодарю.
Привет, подскажите пожалуйста какой Линукс дистрибутив используете?
ZorinOS
Спасибо
Какой у тебя шрифт в ВС коде?
Стандартный, ничего не менял
Еще можно сократить код немного и вместо `numbers = [num for num in range(1_000_000)]` писать `numbers = range(1_000_000)`
Но в первом случае тип объекта list, а во втором range, если нужен list, то просто надо написать так `numbers = list(range(1_000_000))`
Не знаю как это с точки зрения производительности, но по клавишам вы нажимаете меньше, спасибо за внимание)
меньше памяти занимает, range не хранит все эти числа в отличие от list
range это итератор, а с помощью генератора получим список
Странность в Python:
>>> class Test:
def __hash__(self):
return -1
>>> t = Test()
>>> t.__hash__()
-1
>>> hash(t)
-2
>>>
Сам удивился в свое время сравнению скорости.
По результатом получается так: list comprehension, генераторы уже медленнее и в конце map.
Да, но как говорили в комментах ниже, стоит бы еще затраченные ресурсы проверить в дальнейшем
Почему в сравнении суммы и цикла участвует ещё и создание списка? Нельзя его сначала отдельно создать, а потом передать в функцию?
А ещё и вывод в консоль 😂. Не люблю негатив, но таких косяков очень много у автора канала.
Уважаемые комментаторы, которые считают, что нынешних мощностей хватает, чтобы любая программа летала, а особенно ваши калькуляторы. Объясняю, во-первых все подобные замедления суммируются, то есть 0.4 секунды в одном месте, 0.8 в другом, ещё 0.6 в третьем и вот, казалось бы, простая программка из трёх функций, а две лишние секунды потрачены из-за невежества автора кода. Во-вторых, лично мне подобные лайфхаки помогли с нейронными сетями, когда каждая миллисекунда на счету и по сути вручную замеряешь каждый элементы тренировки с целью оптимизации, такие значительные изменения убирают в сумме дни обучения модели
>байтойобить
>байтойобить на питоне
:)
Если вам нужна производительность очень советую посмотреть в сторону numpy. Разница порой набирается на целый порядок
А просто потому что, numpy написан на С, и некоторые свои функции преобразовывает под тот же С, когда свой код остаётся на питоне.
P.s. Для тех, кто в танке, язык "С" работает в разы быстрее, чем питон.
@@z.dekuch1987 язык "С" не работает "в разы быстрее питона" - скомпилированные программы писанные на С работают быстрее чем скрипты интерпретируемые питоном, и не в разы а на порядки быстрее 😉
Если нужна производительность лучше не писать на python
Numpy + Numba тогда.
И можно оставаться на пайтоне.
Обе библиотеки разрабатывались для научных вычислений, как я понял.
Намба особенно впечатляет, код не усложняет и изучать не нужно.
из "тонкостей" только использование кэша.
Последний пример можно реализовать через фильтр по 0 элементу списка по далее вытащить 1 элементы и потом сделать суммирование итога. Получится сильно быстрее.
Вообще, for, while нужно использовать только тогда, когда идёт сложная трансформация данных, для всего остального есть генераторы, встроенные методы переборки вроде map и collections
конкретно тут это не будет быстрее, потому что глупо просеивать список из tuple, а потом суммировать вторые элементы из этих tuple.
Генератор списка и генераторное выражение не отличаются по скорости, отличие в потребление памяти, генератор списка, что очевидно создает список со всеми значениями, а генераторное выражение отдает по одному значению(имеет свойства объекта итеретора).
А еще в качестве примера можно было попробовать while циклы и рекурсии.
для первого примера еще можно попробовать через reduce
Лист-комп - это генератор обернутый в класс лист с помощью [ ]
Лист-комп не может быть быстрее генератора, ибо лист-комп = отработавший до конца генератор + создание объекта класса лист с результатами данных полученных от генератора
В данном случае листкомп быстрее ибо он сравнивается не с генератором, а с генератором обернутым в ( )
То есть по сути тут сравнивается list comprehension и tuple comprehension
Эээ, нет никакого tuple comprehension в природе. Если только явно обернуть в вызов tuple генератор, тогда да, но тут же нет такого, тут обычные генераторные выражения.
@@CrazyElf1971 Вы правы
После низкоуровневых языков программирования меня триггерит то, что использовать готовое решение с точки зрения оптимизации выгоднее, чем писать самому
Привет. Можно спросить не по теме видео?) Я вот пытаюсь парсить крипто сайты, но содержимое формирует JS код. Подскажи куда двигаться, чтобы максимально быстро захватить результат. Пробовал requests-html использовать, но после рендера результат тот же (без подгружаемого контента).
Селениум. Есть бесплатный курс на канале
@@zproger Понял, пасиба. С селениум и seleniumwire знаком. Но думал, что может более быстрые варианты есть.
я конечно нечего ещё не понимаю что тут сказано но было очень интересно
Через пол года попросите чатгпт ускорить ваш код и он сам все оптимизирует. Пока только в лимит токенов упирается. А что будет если скомпилировать код? Будут ли вообще различия в скорости?
Питон не компилируется
@@zproger питон можно компилить)
успехов тебе, желаю в этом году набрать 1кк подписчиков)
Спасибо, постараюсь набрать :D
Я , конечно, не программист, но если надо обрабатывать миллион и более записей, то лучше использовать какую-нибудь sql базу данных, а там уже своя встроенная оптимизация запросов.
Еще быстрее код -
def cycle_example():
n = 999999
total_sum = (n * (n + 1)) // 2
print(total_sum)
Можно дулать просто list(range(1_000_000))
Или может ваш пример как то лучше?
Вот очень интересно, зачем такой масштаб? Вы с телефона кодите или что.
Потому что есть люди которые смотрят с телефонов, и там ничего не видно с мелким шрифтом
кем ты работаешь? Что за сборка линукса?
ZorinOS
Как хорошо, что теперь код к видео можно не самому писать, а с помощью чата генерить, да?)
я это сам писал, что-то не совсем понял прикол)
@@zproger он про chatGPT
а что быстрее крутится списки и циклы или pandas когда 1к + данных?
Спасибо, я очень рад, обьяснение прекрасно.
Благодарю
А какая операционка у вас?
Zorin OS
@@zproger не хотели бы сделать на нее обзор с упором именно на ваши задачи?
А как смотреть этот байткод? Я возможно прослушал в видео
d = {1: 'foo', True: 'bar'}
print(d) # {1: 'bar'}
Спасибо, очень крутое объяснение у тебя!
Все же интересно, почему генератор получился медленнее лист компликейшина...
Возможно сделаю видео с тестами
Потому что плюс генератора в занимаемой памяти, а не в скорости.
Есть ощущение, что вся разница вовсе не от записи.
При запуске, вы не можете гарантировать, что код получил процессорное время в момент старта, и на всё время выполнения, без пауз.
Вспомните про gil, и как ос раздает время процессора.
Итого. На 10⁶+ запусков цикла можно посмотреть среднее.
Чтобы понять, есть ли смысл в этой оптимизации.
И уже тогда думать, как городить тесты "в вакууме", для корректного сравнения.
молодцом, парень, толково объясняешь
Благодарю
list(set[x for x in data if x is not None]) и такой вариант list({x for x in data if x is not None}]
Фига питонисты узнали про функциональное программирование)))
Да :D
а как в Пандасе циклов избежать. Особенно если используешь .loc ?
for key in dict работает быстрее чем for key in dict.keys(). Как я понял это потому что первое выражение использует кэшированные данные, когда как метод keys() возвращает генератор.
Будет ли разница в скорости или затрате памяти если мы передадим глобальную переменную в функцию типо "def function(переменная)" или через global?
не знаю, нужно тестировать :)
А имеет ли это значение? В любом случае глобальная переменная намекает на проблемы в коде
Поскольку все большие объекты передаются по ссылке, то разницы не должно быть вообще. И то и другое просто ссылка.
@@CrazyElf1971 В питоне даже числа передаются по ссылке.
зачем использовать enumerate, когда нужны одни значения?
обычный итератор по значениям будет быстрее: for v in numbers: temp += v
ну а лучше вообще так: for v in range(1_000_000): temp += v
а еще лучше так: sum(range(1_000_000))
в последнем примере быстрее всего будет вообще так (кстати, используется генератор):
print(sum(value for action, value in user_buy if action == "confirmed"))
и это быстрее, чем использование списка (я уж молчу про использование памяти):
print(sum([value for action, value in user_buy if action == "confirmed"]))
как лучше писать рекурсию: через math, рекурсию, или через фор?
Если уж до конца идти, то что в первом примере не сделать sum(range(1_000_000))?
Ну или (1_000_000 * (1_000_000 + 1)) // 2 - 1_000_000?
А в зависимости от железа один и тот же код, например на разных процессорах может выполняться разное время? Например процессоры с разными наборами инструкции?
Даже более того, в разных версиях питона один код может довольно разное время исполняться.
Если количество итераций цикла поставить 10**10 или 10000000000, первый вариант будет работать медленнее)
Привет. Я новый подписчик и всего 2 года изучаю все это. Сейчас перешел на линукс. Расскажи пожалуйста, как ты сделал или собрал себе code oss
Просто говорят, что в коде обычном телеметрия и т.д. Покажешь как такую как у тебя поставить?
чтобы записи были быстрее надо сменить раскладку с "py" на "c++", и тогда уже можно обложиться костылями и летать всё будет как боинг
Странно, я повторил первый пример и на проверке значения range в 100 миллионов, у меня выходит:
Цикл: 8.467104000039399 сек,
sum: 8.498224299983121 сек
То есть, цикл оказывается быстрее.
Но если указать значение в 200 миллионов, то картина меняется на противоположную и sum оказывается быстрее:
Цикл: 17.2555661998922
sum: 16.959907999960706
Теперь sum побеждает.
Проверял множество раз, соотношение и тенденция остаётся таким же
upd: проверил на сотнях повторений и усреднении через timeit, цикл почти всегда побеждает по скорости. Хотя я прям в точности повторил и перепроверил. Да и тут сложно ошибиться... Очень странно
Цикл фактически написан на C/C++ поэтому он должен быстрее работать
P.S. Может ошибаюсь, но это точно связано с C/C++
Отвечаю поздно, но sum тоже на С, а for частично
@@hsqlk
Есть одна проблема, когда без индекса не обойтись - когда вы планируете изменить значения в списке. И enumerate и проход по элементам создают копию, с которой вы работаете, сам элемент не меняется
Но при этом enumerate даёт вам и индекс элемента, вы можете этим индексом воспользоваться, чтобы поменять значение в списке.
Используйте встроенные функции, а в коде [num for num in range(1000)] вместо list(range(1000)), причём это заполнение ещё и в замер попадает.
И вообще, сумма рейнджа решается формулой.
можно пример формулы?
@@Lokamp_ищи "сумма членов арифметической прогрессии"
Это код который пишет junior----- ?
collections вам в помощь!
Циклы только тогда, когда не справляется collections.
Оказывается для получения суммы элементов не надо писать цикл - афигеть! Вот это новость!
Не забывайте о новичках
да можно вообще через reduce + lambda на сумму захерачить )))
Декоратор лучше использовать для замера времени выполнения
Пример можете написать ?
чем лучше?
@@Keefear def time_counter(func):
def inner(*args, **kwargs):
start_t = time.time_ns()
func(*args, **kwargs)
end_t = time.time_ns()
res = end_t - start_t
print(f'execution time of {func.__name__}: {res} ns')
return inner
@@justkrybik Да просто удобнее и меньше кода нужно
а зачем писать num for num in range() если то же самое возвращает просто функция range()
разница в том что мат либы написаны на С++ и скомпилированы! еще они заранее оптимизированы! так устроен python. даже ели вы тоже напишите на С++ то либа python скорее всего будет все ровно быстрее. еще не затронули тему памяти! разные подход по разному используют память. и это реально важно. хотите очень быстрый цикл пишите его на next. и у вас будет реально очень быстрое решение. писать циклом или использовать либу, зависит строго от задачи! просто сказать тип - "либа круче!" это уровень джуна.. нет понятие "так лучше" или "так хуже!", бывает задача и ее решение.)) вроде тут оптимизирует но там дальше из-за этого подхода может все остановиться и задержка вырастит или наоборот упадет. память выжрет к примеру и ПО подвиснет, зато быстро.))))
Во втором примере получил результат, противоположній результату автора:
9999999
time: 1.33
9999999
time: 1.49
Возможно, у меня более поздняя версия Python, в которой доступ по индексу оптимизирован?
Есть классная книжка по такого рода приколам питона, называется Python Cookbook, автор Дэвид Бизли. Там прям рецепты решения всяких задач описаны. Один из советов, изучить встроенную либу collections,
Давай видос про всю мощь much case и её отличие от простых if elif else.
Спасибо, сделаю
олень безграмотный, "much case" аахахха
[num for num in range(10_000)] медленнее, больше по размеру, больше по байт коду, чем просто [*range(10_000)] или list(range(10_000))
Кстати, да. Всегда когда пишется код "[x for x in ..." его обычно можно заменить на более оптимальный код.
лайк от профи по патону - от СЕООНЛИ
Спасибо
Проверил 2 пример из видео - доступ к элементам списка по индексу. По производительности enumerate самый не производительный способ. Python 3.11.1
Рейтинг получился такой:
1) "for num in numbers:"
2) "for num in range(len(numbers)):"
3) "for index, num in enumerate(numbers):"
Ну потому что два вида объектов так перебирается - и индекс и объекты списка. Но если обе эти сущности используются внутри цикла, то enumerate просто удобен и понятен.
Думаю, народ запускает цикл на суммированием когда нужно использовать оператоh if , разбери пример использования sum map lambda filter
Что если в случаях когда нужен индекс вместо enumerate использовать просто счетчик, например index = 0 , а в цикле index+=1?
Итерироваться через for, но увеличивать индекс, заданный вне цикла? Полагаю, по производительности сопоставимо с enumerate, но выглядеть это будет очень грязно.
Почему не используешь python 11?
лень обновлять :D
Мне кажется или вы многовато говорите для человека с именем канала Z proger
во-первых ZProger если уже на то пошло, во-вторых этому нику 3 года,
во-третьих канал о программировании, и не нужно сюда приписывать свою политику
Если реально за производительность - идите в плюсы
Посыл у автора верный - имея возможность стоит всегда делать выбор в пользу коробочных функций, так как их уже оптимизировали до нас + проверять постулаты на достоверность. Но сама подача материала просто пиздец. Делаем тесты на ОДНОМ замере Карл... А еще история с "байт кодом" позабавила. Возможно, автор понимает что там написано (ощущение что нет), но делать выводы исходя только из количества инструкций это конечно топ. Так сказать разбор от бога.
Есть формула n/2*(n+1), n>= 2
Благодарю
Мораль - не изобретайте велосипед. Учите встроенные функции (хотя бы читайте).