В книге «питон.Чистый код» посвящена была эта тема, а функции zip() в первые увидел в книге «Однострочники Питон» Очень маленькие детали!Очень рад, что автор показывает их Было бы еще интереснее, если ты показал как итераторы работают, создав свой класс(протокол итераторов)
А ведь до этого можно и случайно додуматься, к примеру когда в голову придёт мысль "Хм, ну даже в Microsoft Exel есть SUM. И в паскале на уроках информатики в школе что-то такое вроде тоже было, хоть и в сраку паскаль ибо слишком сложно. А вдруг и здесь тоже получится?")) (Это моя реальная история из жизни, пхахах))
Я знаю о функциях prod и sum, но практически никогда их не использую, так как в большинстве случаев оказывается что в формуле будет не просто сумма и все равно придется писать цикл.
первая задача res = n * (n + 1) // 2 я понимаю что не тема ролика, но молодому программисту обязательно нужно повторять что программист это в первую очередь умный человек который оптимизирует свои алгоритмы поиска решения.
Не используйте таймер как замер скорости, во 1х это показывает что разницу во времени только на вашей конфигурации, во 2х даже если вы запустите 2 раза или более значения будут отличаться, так как слишком много от чего будет зависеть время выполнения(например в данный момент при замере первого алгоритма у вас был простой а при замере второго алгоритма ос полезла проверять обновления или ещё че и все уже не достоверное отличие). Лучше поищите информацию как замерить сложность алгоритма в Пайтон.
Суть не в том, за какое время выполнится код, суть в том, чтобы увидеть разницу между 2 подходами. Он написал и запустил оба варианта одновременно, а это значит, что замер покажет разницу правильно. Ну, конечно, если вдруг компьютер не запустит все обновления и не начнёт искать вирусы ровно в тот момент, когда ты запускаешь код, получается ровно через 0.7 секунды))
На 4:25 сначала один байт код enumirate, а в следующем кадре уже сравнивается с другим байткодом. При этом если сравнить первоначально показанный байткод, то для enumirate это 5 инструкций, а для for это всего 3, если считать от FOR_ITER до JUMP_BACKWARD. Интересно было бы сравнить enumirate с простым циклом for num in numbers с инкремент внутри переменной счётчика i объявленной вне цикла.
Время затрачиваемое на работу зависит от кучи переменных, тестить нужно не 1 раз, а запускать код раз 100 и считать среднюю, а если требуется сложная логика, то генераторами с ума сойдёшь её программировать.
"Сейчас я вам покажу, как не надо использовать циклы for". В первых двух примерах создает списки через list comprehension, вместо обычного list(range(1000000)), на который тратится еще меньше времени. Более того, если продолжить, то первый пример и вовсе абсурден, ведь функции sum() не нужен список, ей нужен итерируемый объект. Так что все заканчивается как: sum(range(1000000)). И тратится на это в 3 раза меньше времени, чем на финальный вариант автора.
Спасибо за видео, хоть и знал про эти методы, но лишним не будет. Про zip вообще уже забыл, когда изучал python пару раз использовал, с тех пор вообще о нем забыл. И кстати в следующий раз декоратор для замера времени используй, чтобы меньше кода было, просто не все сразу поймут просматривая это. А так все чётко.
Какой хороший питон, Я щас с# изучаю, и в начале не мог привыкнуть к этим ;, {}, строгой типизации, void, множество типов данных. Но про питон тоже не забываю.
@@nakidai потому что не привык задавать типы, к тому же каждый раз писать void, если метод ничего не возвращает. Я привык в питоне к примеру написал функцию которая просто выполняет какую-то задачу, но ничего не возвращает. Думаю, это из-за привычек, и void по началу Я забывал писать чаще, чем те же ;
@@ismailisabekov8424 Изучал С++ школьником, почти 20 лет назад уже. Сейчас всё делаю на питоне. Блин, как же это красиво: строгая типизация у всего в том числе у функий, приватные и публичные члены классов. Мой внутренний перфекционист часто негодует, что этого нет в питоне. Особенно бесит, что содержимое классов можно менять извне.
То есть, вкратце, пользуйтесь sum для суммирования списка, индексы берите из enumеrate(верно только на чтение), листы склеивайте параллельно zip-ом и по возможности фильтруйте данные в временный список перед работой с ними. Так-то все логично, но в учебниках не особо говорят о том что for по индексам медленный.
Дружище. Насчёт второго примера. 1) Повторил код твоих функций из примера пуля в пулю. У меня 100% быстрее работает с индексами и для меня это не откровение - enumerate очень долгая штука (по крайней мере до 3.10 питона включительно) - я это не однократно проверял, могу куда угодно пруфы скинуть. 2) говорить что байткода выполняется меньше, значит и код выполняется быстрее - неверно. Меньше кода это не всегда быстрее. Как раз-таки оптимизация чаще всего и приводит к увеличению кода - типа в рекурсивную функцию для вычисления чисел фиббоначи добавить кеширование вычисленных чисел - кода становится больше, а скорость вычисления улетает в космос. Больше действий не значит быстрее. Зависит от скорости каждого действия в обоих множествах действий. Ещё раз говорю - куда угодно могу скинуть пруфы что с индексами быстрее работает. И да, байткода мы там так и не увидели. Представленное является дизассемблированным кодом питона, это не байт-код, это набор ассемблерных инструкций
обычно когда доходит до оптимизации кода, проблема не в том, что цикл работает в 1.5-2 раза медленнее, а в том какими структурами данных ты пользуешься. Но с точки зрения написания кода, да примеры из видео как минимум читать приятнее, потому что Pythonic way
Спасибо большое за видео🙂 Давай побольше такого рода сравнений, а то тема, что python медленный очень сильно засела в башке у людей моего окружения. Вот хотя бы такими видео буду потихоньку развивать у них сомнения
Если они пишут на плюсах или расте , мало что у тебя получится, хотя есть синтетические примеры где питон обгонят плюса. Правда там обычно используются написанные отцами на тех плюсах библиотеки питона против банального авторскогобыдлокода на плюсах. Прелесть питона не в скорости, и там где она реально нужна на нем писать никогда не будут, будут на расте. Питон нужен для скорости/дешевизны разработки и поддержки
@@mikeofs1304 проблема в том, что большинство людей используют питон пяткой правой ноги. Пример из жизни. Либа для аналитики. Питон. Работа с таблицами. Вместо сортировки pandas, которая оптимизирована для этих целей -- пузырек. Нашел случайно, когда на 10+млн строках ноут уже помирал... Как по мне -- такое надо показывать. Т.к. те, кто не знаком с питоном будут решать задачу стандартными алгоритмами, когда есть готовые либы "написанные отцами на тех плюсах" или те же sum(list). Пустяк, но целому отделу аналитики, работающему на тб данных работу ускорит
генераторы будут лучше по времени если ожидается получения негативного условия в выражении формирования. Здесь же в примере все значения участвуют, пример в пользу списочного выражения. Но разница все равно копеечная, экономия в памяти все равно выводит выражения генераторы на первое место.
Еще можно сократить код немного и вместо `numbers = [num for num in range(1_000_000)]` писать `numbers = range(1_000_000)` Но в первом случае тип объекта list, а во втором range, если нужен list, то просто надо написать так `numbers = list(range(1_000_000))` Не знаю как это с точки зрения производительности, но по клавишам вы нажимаете меньше, спасибо за внимание)
1-ый пример совершенно притянутый за уши, я такого в реальной жизни никогда не видел. Давайте возьмем какую-нибудь вложенную структуру, в ней что-то найдём (причём найдём как-то не так чтобы очень просто, с переходом в другие процедуры) и оттуда уже суммируем данные по циклу. Впрочем, 4-й пример именно об этом. 2-ой, да согласен, это так и есть. 3-й, согласен, zip -- сила. ))) 4-й, тоже согласен. Разница как бы не такая большая, если процедура выполняется один раз, но если оно балалайкой повторяется миллион раз в цикле... )))
Я , конечно, не программист, но если надо обрабатывать миллион и более записей, то лучше использовать какую-нибудь sql базу данных, а там уже своя встроенная оптимизация запросов.
Лист-комп - это генератор обернутый в класс лист с помощью [ ] Лист-комп не может быть быстрее генератора, ибо лист-комп = отработавший до конца генератор + создание объекта класса лист с результатами данных полученных от генератора В данном случае листкомп быстрее ибо он сравнивается не с генератором, а с генератором обернутым в ( ) То есть по сути тут сравнивается list comprehension и tuple comprehension
Эээ, нет никакого tuple comprehension в природе. Если только явно обернуть в вызов tuple генератор, тогда да, но тут же нет такого, тут обычные генераторные выражения.
У меня есть 2 функции для деления текста на страницы. обрезание страницы было реализовано через цикл фор как раз по индексам. Заменил рейндж лен на енумерейт время выполнения функции возросло в 2 раза
Генератор списка и генераторное выражение не отличаются по скорости, отличие в потребление памяти, генератор списка, что очевидно создает список со всеми значениями, а генераторное выражение отдает по одному значению(имеет свойства объекта итеретора). А еще в качестве примера можно было попробовать while циклы и рекурсии.
Последний пример можно реализовать через фильтр по 0 элементу списка по далее вытащить 1 элементы и потом сделать суммирование итога. Получится сильно быстрее. Вообще, for, while нужно использовать только тогда, когда идёт сложная трансформация данных, для всего остального есть генераторы, встроенные методы переборки вроде map и collections
зачем использовать 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"]))
Жалкие потуги оптимизации кода на языке пайтон, который изначально был создан не про оптимизацию и не про доскональное понимание внутренней кухни процессора. Странные люди сначала забивают на типы данных, предоставляют языку самому решать где и как что хранить и перемещать, а потом задаются вопросом, а почему это так долго.
После низкоуровневых языков программирования меня триггерит то, что использовать готовое решение с точки зрения оптимизации выгоднее, чем писать самому
Всё время удивляюсь как кто-то пытается поумничать, но это выглядит достаточно клоунски... Во-первых, приведённые примеры не везде применимы. Во-вторых фанатизм с внутренним функционалом языков программирования может приводить к непредсказуемым результатам и даже потери контроля. К примеру, был на практике случай, когда я писал собственную библиотеку даже для стандартных функций +-/*. Как бы это смешно не звучало, но нужно былл получать точный результат, а стандартные функции достаточно не точно считали после запятой и после огромного количества расчётов неточность уже была значительной даже в целой части чисел. В итоге библиотека не была ограничена ни в точности, ни в размерах чисел. Так же фанатизм з функционалом языков программирования практически убивает совместимость и придётся значительно переписывать код при миграции на новые версии. И в-третьих, не так это сильно быстрее, чтобы заморачиваться. Нужно быстрее - заморачивайся с компилятором Си и пересобери Python нормально.
разница в том что мат либы написаны на С++ и скомпилированы! еще они заранее оптимизированы! так устроен python. даже ели вы тоже напишите на С++ то либа python скорее всего будет все ровно быстрее. еще не затронули тему памяти! разные подход по разному используют память. и это реально важно. хотите очень быстрый цикл пишите его на next. и у вас будет реально очень быстрое решение. писать циклом или использовать либу, зависит строго от задачи! просто сказать тип - "либа круче!" это уровень джуна.. нет понятие "так лучше" или "так хуже!", бывает задача и ее решение.)) вроде тут оптимизирует но там дальше из-за этого подхода может все остановиться и задержка вырастит или наоборот упадет. память выжрет к примеру и ПО подвиснет, зато быстро.))))
for key in dict работает быстрее чем for key in dict.keys(). Как я понял это потому что первое выражение использует кэшированные данные, когда как метод keys() возвращает генератор.
Проверил 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 просто удобен и понятен.
А просто потому что, numpy написан на С, и некоторые свои функции преобразовывает под тот же С, когда свой код остаётся на питоне. P.s. Для тех, кто в танке, язык "С" работает в разы быстрее, чем питон.
@@z.dekuch1987 язык "С" не работает "в разы быстрее питона" - скомпилированные программы писанные на С работают быстрее чем скрипты интерпретируемые питоном, и не в разы а на порядки быстрее 😉
Numpy + Numba тогда. И можно оставаться на пайтоне. Обе библиотеки разрабатывались для научных вычислений, как я понял. Намба особенно впечатляет, код не усложняет и изучать не нужно. из "тонкостей" только использование кэша.
Мне стало непонятно, почему в первом примере вместо numbers = [num for num in range(1_000_000)] не использовать просто numbers = range(1_000_000) и потом точно так же вывести sum(numbers)? Интересно было бы узнать причину.
Есть ощущение, что вся разница вовсе не от записи. При запуске, вы не можете гарантировать, что код получил процессорное время в момент старта, и на всё время выполнения, без пауз. Вспомните про gil, и как ос раздает время процессора. Итого. На 10⁶+ запусков цикла можно посмотреть среднее. Чтобы понять, есть ли смысл в этой оптимизации. И уже тогда думать, как городить тесты "в вакууме", для корректного сравнения.
3:50 lst = [choice(ascii_letters + digits + punctuation) for i in range(100_000_000)] t1 = time() for i in range(len(lst)): a = lst[i] print("Обращение по индексу и range:", time() - t1) t1 = time() for i, val in enumerate(lst): a = val print("Enumerate", time() - t1) не знаю почему, но у меня выдает следующие значения: Обращение по индексу и range: 4.414041996002197 Enumerate 5.103984832763672 То есть enumerate медленнее, а в видео наоборот, может из-за того что я пишу на pycharme, а вы в vs code?
Занятное видео. Только хотелось бы видеть реальные примеры. А не оторванные от жизни. И нет ответа: чем конкретно заменить FOR когда нужно именно перебрать массивы? Единственный плюс -- enumerate. но тоже как-то сильно принижен. Плюс enumerate именно в отсутствии потребности дополнительно заниматься индексами. А не в отличии скорости выполнения. бОльшая часть показанного -- просто странные алгоритмы. Последние примеры вообще не "питон вей". Если нужно тупо сложить два массива, то нужно и складывать два массива. А не перебирать их. например: c: np.ndarray = a: np.ndarray + b: nd.ndarray. И никаких for ) или list = pd.where(a['status'] == confirmed). и тоже никаких форов ) Так же, код генератора не показан. Т.ч. делать какие-либо выводы смысла нет. Вполне вероятно, что он наговнокоден. А массив легко влезает в память и/или вообще не требует такого подхода. Не говоря уже о том, что если нужно сложить массивы по фильтру, то опять же, нужно использовать штатные методы работы со срезами и фильтрами (=, >
Используйте встроенные функции, а в коде [num for num in range(1000)] вместо list(range(1000)), причём это заполнение ещё и в замер попадает. И вообще, сумма рейнджа решается формулой.
Через пол года попросите чатгпт ускорить ваш код и он сам все оптимизирует. Пока только в лимит токенов упирается. А что будет если скомпилировать код? Будут ли вообще различия в скорости?
Странно, я повторил первый пример и на проверке значения range в 100 миллионов, у меня выходит: Цикл: 8.467104000039399 сек, sum: 8.498224299983121 сек То есть, цикл оказывается быстрее. Но если указать значение в 200 миллионов, то картина меняется на противоположную и sum оказывается быстрее: Цикл: 17.2555661998922 sum: 16.959907999960706 Теперь sum побеждает. Проверял множество раз, соотношение и тенденция остаётся таким же upd: проверил на сотнях повторений и усреднении через timeit, цикл почти всегда побеждает по скорости. Хотя я прям в точности повторил и перепроверил. Да и тут сложно ошибиться... Очень странно
Уважаемые комментаторы, которые считают, что нынешних мощностей хватает, чтобы любая программа летала, а особенно ваши калькуляторы. Объясняю, во-первых все подобные замедления суммируются, то есть 0.4 секунды в одном месте, 0.8 в другом, ещё 0.6 в третьем и вот, казалось бы, простая программка из трёх функций, а две лишние секунды потрачены из-за невежества автора кода. Во-вторых, лично мне подобные лайфхаки помогли с нейронными сетями, когда каждая миллисекунда на счету и по сути вручную замеряешь каждый элементы тренировки с целью оптимизации, такие значительные изменения убирают в сумме дни обучения модели
Есть классная книжка по такого рода приколам питона, называется Python Cookbook, автор Дэвид Бизли. Там прям рецепты решения всяких задач описаны. Один из советов, изучить встроенную либу collections,
"Использование встроенных функций python на примере цикла FOR" Такой залоговок больше бы подошел О том КАК писать циклы FOR, и уж тем более о том какие практики предпочтительнее, - в видео ни слова :)
Если я напишу такой заголовок, то видео наберет 2 просмотра к сожалению. Ну и почему же нет ни слова, я показываю в каких случаях for можно эффективно заменить, сравнивая с обычными примерами. Все сходится.
Есть одна проблема, когда без индекса не обойтись - когда вы планируете изменить значения в списке. И enumerate и проход по элементам создают копию, с которой вы работаете, сам элемент не меняется
Привет. Можно спросить не по теме видео?) Я вот пытаюсь парсить крипто сайты, но содержимое формирует JS код. Подскажи куда двигаться, чтобы максимально быстро захватить результат. Пробовал requests-html использовать, но после рендера результат тот же (без подгружаемого контента).
Во втором примере получил результат, противоположній результату автора: 9999999 time: 1.33 9999999 time: 1.49 Возможно, у меня более поздняя версия Python, в которой доступ по индексу оптимизирован?
Затраты памяти ещё сравните у функций с генератором и с листом в 4 примере
хорошая идея, в следующих видео добавлю и затраты ресурсов для наглядности
@@zproger и сразу станет видно, что не в скорости прелесть генератора :-)
@@splinter928 в этом конкретном случае не станет
@@dmitriyneledva4693 Из-за того что немного памяти занимает эти данные?
Пишите на ассемблере
В книге «питон.Чистый код» посвящена была эта тема, а функции zip() в первые увидел в книге «Однострочники Питон»
Очень маленькие детали!Очень рад, что автор показывает их
Было бы еще интереснее, если ты показал как итераторы работают, создав свой класс(протокол итераторов)
Здравствуйте, очень полезное видео, спасибо вам за добро которые вы делаете когда делитесь с знаниями
Благодарю за комментарий! Рад что видео принесло вам пользу!
На мой взгляд - если чел использует цикл 'For' вместо 'Sum' то это просто незнание встроенных команд / функций и т.д.
А ведь до этого можно и случайно додуматься, к примеру когда в голову придёт мысль "Хм, ну даже в Microsoft Exel есть SUM. И в паскале на уроках информатики в школе что-то такое вроде тоже было, хоть и в сраку паскаль ибо слишком сложно. А вдруг и здесь тоже получится?"))
(Это моя реальная история из жизни, пхахах))
Я знаю о функциях prod и sum, но практически никогда их не использую, так как в большинстве случаев оказывается что в формуле будет не просто сумма и все равно придется писать цикл.
Да нет ,это дает разнообразие решения задач
@@13-th_Lord паскаль слишком сложно? нууууу... вроде самый простой и логичный язык. синтаксис у него конечно так себе...
первая задача res = n * (n + 1) // 2 я понимаю что не тема ролика, но молодому программисту обязательно нужно повторять что программист это в первую очередь умный человек который оптимизирует свои алгоритмы поиска решения.
Лучший! Спасибо за качественный контент
Спасибо
Не используйте таймер как замер скорости, во 1х это показывает что разницу во времени только на вашей конфигурации, во 2х даже если вы запустите 2 раза или более значения будут отличаться, так как слишком много от чего будет зависеть время выполнения(например в данный момент при замере первого алгоритма у вас был простой а при замере второго алгоритма ос полезла проверять обновления или ещё че и все уже не достоверное отличие). Лучше поищите информацию как замерить сложность алгоритма в Пайтон.
Ну или надо делать пару тысяч замеров по одной и той же задаче и выводить среднее. Тогда будет более-менее близко к правде
Надо использовать модуль timeit, он запускает фрагмент кода n раз (1000000 по умолчанию)
Суть не в том, за какое время выполнится код, суть в том, чтобы увидеть разницу между 2 подходами. Он написал и запустил оба варианта одновременно, а это значит, что замер покажет разницу правильно. Ну, конечно, если вдруг компьютер не запустит все обновления и не начнёт искать вирусы ровно в тот момент, когда ты запускаешь код, получается ровно через 0.7 секунды))
@@beardedman721замер правильно разницу не покажет, но статически значимые отличия гипотез получим именно в этих случаях…
Спасибо. Я вот уже раз 10 запустил первый пример, и у меня 0.1 время первого цикла, 0.09 - второго.
на первой минуте звучит фраза, что sum - принимает итератор, что абсолютно не верно. sum - принимает iterable, т.е итерируемый объект.
Спасибо за исправление, оговорился
Отличная подача, подписался на канал!
Благодарю
Вот 2 пример - это про меня 😎
😎 😎
Шок контент - этот 🍀клевер приносит удачу, по слухам, если его скопировать и вставить в код, он сделает код оптимизированным и быстрым.
На 4:25 сначала один байт код enumirate, а в следующем кадре уже сравнивается с другим байткодом. При этом если сравнить первоначально показанный байткод, то для enumirate это 5 инструкций, а для for это всего 3, если считать от FOR_ITER до JUMP_BACKWARD. Интересно было бы сравнить enumirate с простым циклом for num in numbers с инкремент внутри переменной счётчика i объявленной вне цикла.
Время затрачиваемое на работу зависит от кучи переменных, тестить нужно не 1 раз, а запускать код раз 100 и считать среднюю, а если требуется сложная логика, то генераторами с ума сойдёшь её программировать.
Благодарю! 🔥👍
Рад что понравилось =))
Спасибо большое за информацию я только только начинаю спасибо
Пожалуйста, желаю успешного изучения!
Крсава! Все четко! Хорошо я так и писал)
Благодарю
"Сейчас я вам покажу, как не надо использовать циклы for".
В первых двух примерах создает списки через list comprehension, вместо обычного list(range(1000000)), на который тратится еще меньше времени.
Более того, если продолжить, то первый пример и вовсе абсурден, ведь функции sum() не нужен список, ей нужен итерируемый объект.
Так что все заканчивается как: sum(range(1000000)). И тратится на это в 3 раза меньше времени, чем на финальный вариант автора.
Спасибо за видео, хоть и знал про эти методы, но лишним не будет. Про zip вообще уже забыл, когда изучал python пару раз использовал, с тех пор вообще о нем забыл. И кстати в следующий раз декоратор для замера времени используй, чтобы меньше кода было, просто не все сразу поймут просматривая это. А так все чётко.
Какой хороший питон, Я щас с# изучаю, и в начале не мог привыкнуть к этим ;, {}, строгой типизации, void, множество типов данных. Но про питон тоже не забываю.
@@ismailisabekov8424 А почему void прям отдельно? 🤔
@@nakidai потому что не привык задавать типы, к тому же каждый раз писать void, если метод ничего не возвращает. Я привык в питоне к примеру написал функцию которая просто выполняет какую-то задачу, но ничего не возвращает. Думаю, это из-за привычек, и void по началу Я забывал писать чаще, чем те же ;
@@ismailisabekov8424 Изучал С++ школьником, почти 20 лет назад уже. Сейчас всё делаю на питоне. Блин, как же это красиво: строгая типизация у всего в том числе у функий, приватные и публичные члены классов. Мой внутренний перфекционист часто негодует, что этого нет в питоне. Особенно бесит, что содержимое классов можно менять извне.
В первом примере можно сократить до print(sum(range(1_000_000)))
Есть такое, об этом писал в телеграмм канале после публикации видео
Спасибо. Ценный контент.
То есть, вкратце, пользуйтесь sum для суммирования списка, индексы берите из enumеrate(верно только на чтение), листы склеивайте параллельно zip-ом и по возможности фильтруйте данные в временный список перед работой с ними.
Так-то все логично, но в учебниках не особо говорят о том что for по индексам медленный.
Дружище. Насчёт второго примера.
1) Повторил код твоих функций из примера пуля в пулю. У меня 100% быстрее работает с индексами и для меня это не откровение - enumerate очень долгая штука (по крайней мере до 3.10 питона включительно) - я это не однократно проверял, могу куда угодно пруфы скинуть.
2) говорить что байткода выполняется меньше, значит и код выполняется быстрее - неверно. Меньше кода это не всегда быстрее. Как раз-таки оптимизация чаще всего и приводит к увеличению кода - типа в рекурсивную функцию для вычисления чисел фиббоначи добавить кеширование вычисленных чисел - кода становится больше, а скорость вычисления улетает в космос. Больше действий не значит быстрее. Зависит от скорости каждого действия в обоих множествах действий. Ещё раз говорю - куда угодно могу скинуть пруфы что с индексами быстрее работает.
И да, байткода мы там так и не увидели. Представленное является дизассемблированным кодом питона, это не байт-код, это набор ассемблерных инструкций
обычно когда доходит до оптимизации кода, проблема не в том, что цикл работает в 1.5-2 раза медленнее, а в том какими структурами данных ты пользуешься. Но с точки зрения написания кода, да примеры из видео как минимум читать приятнее, потому что Pythonic way
Спасибо большое за видео🙂 Давай побольше такого рода сравнений, а то тема, что python медленный очень сильно засела в башке у людей моего окружения. Вот хотя бы такими видео буду потихоньку развивать у них сомнения
.да и еще, как с использованием enumirate найти в списке максимальное кол-во повторяющихся элементов?
@@mak32 Лучше используйте collections.Counter для этого
Спасибо
Если они пишут на плюсах или расте , мало что у тебя получится, хотя есть синтетические примеры где питон обгонят плюса. Правда там обычно используются написанные отцами на тех плюсах библиотеки питона против банального авторскогобыдлокода на плюсах. Прелесть питона не в скорости, и там где она реально нужна на нем писать никогда не будут, будут на расте. Питон нужен для скорости/дешевизны разработки и поддержки
@@mikeofs1304 проблема в том, что большинство людей используют питон пяткой правой ноги.
Пример из жизни. Либа для аналитики. Питон. Работа с таблицами. Вместо сортировки pandas, которая оптимизирована для этих целей -- пузырек. Нашел случайно, когда на 10+млн строках ноут уже помирал... Как по мне -- такое надо показывать. Т.к. те, кто не знаком с питоном будут решать задачу стандартными алгоритмами, когда есть готовые либы "написанные отцами на тех плюсах" или те же sum(list). Пустяк, но целому отделу аналитики, работающему на тб данных работу ускорит
превью это жиза просто. с каждым днём всё лучше, удачи тебе!)
Благодарю, стараюсь 😉
Шишбик,согл)
@@burgershot777 :3
А почему бы не использовать filter + map + sum вместо list comprehension + sum и generator + sum?
я конечно нечего ещё не понимаю что тут сказано но было очень интересно
генераторы будут лучше по времени если ожидается получения негативного условия в выражении формирования. Здесь же в примере все значения участвуют, пример в пользу списочного выражения. Но разница все равно копеечная, экономия в памяти все равно выводит выражения генераторы на первое место.
Еще можно сократить код немного и вместо `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 это итератор, а с помощью генератора получим список
Сам удивился в свое время сравнению скорости.
По результатом получается так: list comprehension, генераторы уже медленнее и в конце map.
Да, но как говорили в комментах ниже, стоит бы еще затраченные ресурсы проверить в дальнейшем
1-ый пример совершенно притянутый за уши, я такого в реальной жизни никогда не видел. Давайте возьмем какую-нибудь вложенную структуру, в ней что-то найдём (причём найдём как-то не так чтобы очень просто, с переходом в другие процедуры) и оттуда уже суммируем данные по циклу. Впрочем, 4-й пример именно об этом.
2-ой, да согласен, это так и есть.
3-й, согласен, zip -- сила. )))
4-й, тоже согласен.
Разница как бы не такая большая, если процедура выполняется один раз, но если оно балалайкой повторяется миллион раз в цикле... )))
Я , конечно, не программист, но если надо обрабатывать миллион и более записей, то лучше использовать какую-нибудь sql базу данных, а там уже своя встроенная оптимизация запросов.
Лист-комп - это генератор обернутый в класс лист с помощью [ ]
Лист-комп не может быть быстрее генератора, ибо лист-комп = отработавший до конца генератор + создание объекта класса лист с результатами данных полученных от генератора
В данном случае листкомп быстрее ибо он сравнивается не с генератором, а с генератором обернутым в ( )
То есть по сути тут сравнивается list comprehension и tuple comprehension
Эээ, нет никакого tuple comprehension в природе. Если только явно обернуть в вызов tuple генератор, тогда да, но тут же нет такого, тут обычные генераторные выражения.
@@CrazyElf1971 Вы правы
У меня есть 2 функции для деления текста на страницы.
обрезание страницы было реализовано через цикл фор как раз по индексам.
Заменил рейндж лен на енумерейт время выполнения функции возросло в 2 раза
Спасибо, я очень рад, обьяснение прекрасно.
Благодарю
Генератор списка и генераторное выражение не отличаются по скорости, отличие в потребление памяти, генератор списка, что очевидно создает список со всеми значениями, а генераторное выражение отдает по одному значению(имеет свойства объекта итеретора).
А еще в качестве примера можно было попробовать while циклы и рекурсии.
Последний пример можно реализовать через фильтр по 0 элементу списка по далее вытащить 1 элементы и потом сделать суммирование итога. Получится сильно быстрее.
Вообще, for, while нужно использовать только тогда, когда идёт сложная трансформация данных, для всего остального есть генераторы, встроенные методы переборки вроде map и collections
конкретно тут это не будет быстрее, потому что глупо просеивать список из tuple, а потом суммировать вторые элементы из этих tuple.
для первого примера еще можно попробовать через reduce
d = {1: 'foo', True: 'bar'}
print(d) # {1: 'bar'}
Еще быстрее код -
def cycle_example():
n = 999999
total_sum = (n * (n + 1)) // 2
print(total_sum)
зачем использовать 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"]))
Какой у тебя шрифт в ВС коде?
Стандартный, ничего не менял
list(set[x for x in data if x is not None]) и такой вариант list({x for x in data if x is not None}]
Подскажи, что у тебя за дистрибутив и какое графическое окружение стоит ?
Zorin OS + дефолт окружение
Жалкие потуги оптимизации кода на языке пайтон, который изначально был создан не про оптимизацию и не про доскональное понимание внутренней кухни процессора. Странные люди сначала забивают на типы данных, предоставляют языку самому решать где и как что хранить и перемещать, а потом задаются вопросом, а почему это так долго.
После низкоуровневых языков программирования меня триггерит то, что использовать готовое решение с точки зрения оптимизации выгоднее, чем писать самому
А что быстрее работает: списки или кортежи? По кортежам тоже можно сумму находить.
Всё время удивляюсь как кто-то пытается поумничать, но это выглядит достаточно клоунски... Во-первых, приведённые примеры не везде применимы. Во-вторых фанатизм с внутренним функционалом языков программирования может приводить к непредсказуемым результатам и даже потери контроля. К примеру, был на практике случай, когда я писал собственную библиотеку даже для стандартных функций +-/*. Как бы это смешно не звучало, но нужно былл получать точный результат, а стандартные функции достаточно не точно считали после запятой и после огромного количества расчётов неточность уже была значительной даже в целой части чисел. В итоге библиотека не была ограничена ни в точности, ни в размерах чисел. Так же фанатизм з функционалом языков программирования практически убивает совместимость и придётся значительно переписывать код при миграции на новые версии. И в-третьих, не так это сильно быстрее, чтобы заморачиваться. Нужно быстрее - заморачивайся с компилятором Си и пересобери Python нормально.
разница в том что мат либы написаны на С++ и скомпилированы! еще они заранее оптимизированы! так устроен python. даже ели вы тоже напишите на С++ то либа python скорее всего будет все ровно быстрее. еще не затронули тему памяти! разные подход по разному используют память. и это реально важно. хотите очень быстрый цикл пишите его на next. и у вас будет реально очень быстрое решение. писать циклом или использовать либу, зависит строго от задачи! просто сказать тип - "либа круче!" это уровень джуна.. нет понятие "так лучше" или "так хуже!", бывает задача и ее решение.)) вроде тут оптимизирует но там дальше из-за этого подхода может все остановиться и задержка вырастит или наоборот упадет. память выжрет к примеру и ПО подвиснет, зато быстро.))))
for key in dict работает быстрее чем for key in dict.keys(). Как я понял это потому что первое выражение использует кэшированные данные, когда как метод keys() возвращает генератор.
Проверил 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 просто удобен и понятен.
Это база!
Больше всего обидно, что реьята, которые сейчас монут учиться не учатся, а сразу бегут в разработку
практика самое лучше обучение :)
Если вам нужна производительность очень советую посмотреть в сторону numpy. Разница порой набирается на целый порядок
А просто потому что, numpy написан на С, и некоторые свои функции преобразовывает под тот же С, когда свой код остаётся на питоне.
P.s. Для тех, кто в танке, язык "С" работает в разы быстрее, чем питон.
@@z.dekuch1987 язык "С" не работает "в разы быстрее питона" - скомпилированные программы писанные на С работают быстрее чем скрипты интерпретируемые питоном, и не в разы а на порядки быстрее 😉
Если нужна производительность лучше не писать на python
Numpy + Numba тогда.
И можно оставаться на пайтоне.
Обе библиотеки разрабатывались для научных вычислений, как я понял.
Намба особенно впечатляет, код не усложняет и изучать не нужно.
из "тонкостей" только использование кэша.
Мне стало непонятно, почему в первом примере вместо numbers = [num for num in range(1_000_000)] не использовать просто
numbers = range(1_000_000) и потом точно так же вывести sum(numbers)? Интересно было бы узнать причину.
лайк от профи по патону - от СЕООНЛИ
Спасибо
Почему в сравнении суммы и цикла участвует ещё и создание списка? Нельзя его сначала отдельно создать, а потом передать в функцию?
А ещё и вывод в консоль 😂. Не люблю негатив, но таких косяков очень много у автора канала.
Все равно это очень долго, если уж говорить про скорость, то
n*(n+1)/2
Сработает очень быстро
Есть ощущение, что вся разница вовсе не от записи.
При запуске, вы не можете гарантировать, что код получил процессорное время в момент старта, и на всё время выполнения, без пауз.
Вспомните про gil, и как ос раздает время процессора.
Итого. На 10⁶+ запусков цикла можно посмотреть среднее.
Чтобы понять, есть ли смысл в этой оптимизации.
И уже тогда думать, как городить тесты "в вакууме", для корректного сравнения.
Как хорошо, что теперь код к видео можно не самому писать, а с помощью чата генерить, да?)
я это сам писал, что-то не совсем понял прикол)
@@zproger он про chatGPT
Странность в Python:
>>> class Test:
def __hash__(self):
return -1
>>> t = Test()
>>> t.__hash__()
-1
>>> hash(t)
-2
>>>
3:50
lst = [choice(ascii_letters + digits + punctuation) for i in range(100_000_000)]
t1 = time()
for i in range(len(lst)):
a = lst[i]
print("Обращение по индексу и range:", time() - t1)
t1 = time()
for i, val in enumerate(lst):
a = val
print("Enumerate", time() - t1)
не знаю почему, но у меня выдает следующие значения:
Обращение по индексу и range: 4.414041996002197
Enumerate 5.103984832763672
То есть enumerate медленнее, а в видео наоборот, может из-за того что я пишу на pycharme, а вы в vs code?
идешки вроде не влияют на скорость выполнения кода
Привет, подскажите пожалуйста какой Линукс дистрибутив используете?
ZorinOS
Спасибо
Занятное видео. Только хотелось бы видеть реальные примеры. А не оторванные от жизни.
И нет ответа: чем конкретно заменить FOR когда нужно именно перебрать массивы?
Единственный плюс -- enumerate. но тоже как-то сильно принижен.
Плюс enumerate именно в отсутствии потребности дополнительно заниматься индексами. А не в отличии скорости выполнения.
бОльшая часть показанного -- просто странные алгоритмы.
Последние примеры вообще не "питон вей".
Если нужно тупо сложить два массива, то нужно и складывать два массива. А не перебирать их.
например: c: np.ndarray = a: np.ndarray + b: nd.ndarray. И никаких for )
или list = pd.where(a['status'] == confirmed). и тоже никаких форов )
Так же, код генератора не показан.
Т.ч. делать какие-либо выводы смысла нет.
Вполне вероятно, что он наговнокоден. А массив легко влезает в память и/или вообще не требует такого подхода.
Не говоря уже о том, что если нужно сложить массивы по фильтру, то опять же, нужно использовать штатные методы работы со срезами и фильтрами (=, >
Если количество итераций цикла поставить 10**10 или 10000000000, первый вариант будет работать медленнее)
Доброго времени суток. Какую программу используюте для написания кода?
Vs Code
@@zproger благодарю.
Используйте встроенные функции, а в коде [num for num in range(1000)] вместо list(range(1000)), причём это заполнение ещё и в замер попадает.
И вообще, сумма рейнджа решается формулой.
можно пример формулы?
@@Lokamp_ищи "сумма членов арифметической прогрессии"
Думаю, народ запускает цикл на суммированием когда нужно использовать оператоh if , разбери пример использования sum map lambda filter
А какая операционка у вас?
Zorin OS
@@zproger не хотели бы сделать на нее обзор с упором именно на ваши задачи?
Спасибо, очень крутое объяснение у тебя!
Все же интересно, почему генератор получился медленнее лист компликейшина...
Возможно сделаю видео с тестами
Потому что плюс генератора в занимаемой памяти, а не в скорости.
Через пол года попросите чатгпт ускорить ваш код и он сам все оптимизирует. Пока только в лимит токенов упирается. А что будет если скомпилировать код? Будут ли вообще различия в скорости?
Питон не компилируется
@@zproger питон можно компилить)
Странно, я повторил первый пример и на проверке значения 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
Можно дулать просто list(range(1_000_000))
Или может ваш пример как то лучше?
кем ты работаешь? Что за сборка линукса?
ZorinOS
Уважаемые комментаторы, которые считают, что нынешних мощностей хватает, чтобы любая программа летала, а особенно ваши калькуляторы. Объясняю, во-первых все подобные замедления суммируются, то есть 0.4 секунды в одном месте, 0.8 в другом, ещё 0.6 в третьем и вот, казалось бы, простая программка из трёх функций, а две лишние секунды потрачены из-за невежества автора кода. Во-вторых, лично мне подобные лайфхаки помогли с нейронными сетями, когда каждая миллисекунда на счету и по сути вручную замеряешь каждый элементы тренировки с целью оптимизации, такие значительные изменения убирают в сумме дни обучения модели
Вот очень интересно, зачем такой масштаб? Вы с телефона кодите или что.
Потому что есть люди которые смотрят с телефонов, и там ничего не видно с мелким шрифтом
Есть классная книжка по такого рода приколам питона, называется Python Cookbook, автор Дэвид Бизли. Там прям рецепты решения всяких задач описаны. Один из советов, изучить встроенную либу collections,
"Использование встроенных функций python на примере цикла FOR"
Такой залоговок больше бы подошел
О том КАК писать циклы FOR, и уж тем более о том какие практики предпочтительнее, - в видео ни слова :)
Если я напишу такой заголовок, то видео наберет 2 просмотра к сожалению.
Ну и почему же нет ни слова, я показываю в каких случаях for можно эффективно заменить, сравнивая с обычными примерами. Все сходится.
@@zproger Гы-гы. Поэтому врёте? Мило.... "Народ, я тут кликбейтом занимаюсь...."
Никакого кликбейта нет, обложка и название соответствуют контенту
@@zproger Разумеется, не соответствуют, о чём выше вполне ясно написали.
Привет. Я новый подписчик и всего 2 года изучаю все это. Сейчас перешел на линукс. Расскажи пожалуйста, как ты сделал или собрал себе code oss
Просто говорят, что в коде обычном телеметрия и т.д. Покажешь как такую как у тебя поставить?
Мораль - не изобретайте велосипед. Учите встроенные функции (хотя бы читайте).
Интересно, а паттерн мэтч с вычислимыми полями работает быстрее условных операторов?..
Enumerate кайфовая штука для создания платформера
Есть одна проблема, когда без индекса не обойтись - когда вы планируете изменить значения в списке. И enumerate и проход по элементам создают копию, с которой вы работаете, сам элемент не меняется
Но при этом enumerate даёт вам и индекс элемента, вы можете этим индексом воспользоваться, чтобы поменять значение в списке.
[num for num in range(10_000)] медленнее, больше по размеру, больше по байт коду, чем просто [*range(10_000)] или list(range(10_000))
Кстати, да. Всегда когда пишется код "[x for x in ..." его обычно можно заменить на более оптимальный код.
чтобы записи были быстрее надо сменить раскладку с "py" на "c++", и тогда уже можно обложиться костылями и летать всё будет как боинг
успехов тебе, желаю в этом году набрать 1кк подписчиков)
Спасибо, постараюсь набрать :D
Привет. Можно спросить не по теме видео?) Я вот пытаюсь парсить крипто сайты, но содержимое формирует JS код. Подскажи куда двигаться, чтобы максимально быстро захватить результат. Пробовал requests-html использовать, но после рендера результат тот же (без подгружаемого контента).
Селениум. Есть бесплатный курс на канале
@@zproger Понял, пасиба. С селениум и seleniumwire знаком. Но думал, что может более быстрые варианты есть.
Оказывается для получения суммы элементов не надо писать цикл - афигеть! Вот это новость!
Не забывайте о новичках
да можно вообще через reduce + lambda на сумму захерачить )))
как лучше писать рекурсию: через math, рекурсию, или через фор?
Если уж до конца идти, то что в первом примере не сделать sum(range(1_000_000))?
Ну или (1_000_000 * (1_000_000 + 1)) // 2 - 1_000_000?
Есть формула n/2*(n+1), n>= 2
Благодарю
Фига питонисты узнали про функциональное программирование)))
Да :D
2:57 написано for i in number, а массив называется numbers
Давай видос про всю мощь much case и её отличие от простых if elif else.
Спасибо, сделаю
олень безграмотный, "much case" аахахха
а как в Пандасе циклов избежать. Особенно если используешь .loc ?
В первом примере можно сделать еще быстрее и короче. sum(range(1_000_000))
согласен
а что быстрее крутится списки и циклы или pandas когда 1к + данных?
Декоратор лучше использовать для замера времени выполнения
Пример можете написать ?
чем лучше?
@@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()
молодцом, парень, толково объясняешь
Благодарю
Я думал enumerate наоборот затратнее и медленнее, поэтому редко использовал его.
Во втором примере получил результат, противоположній результату автора:
9999999
time: 1.33
9999999
time: 1.49
Возможно, у меня более поздняя версия Python, в которой доступ по индексу оптимизирован?