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