«За что вы так меня не любите?» (с) Python -4


AliExpress RU&CIS

Python уже на протяжении нескольких лет держится в топе языков программирования. А любая популярная вещь так или иначе обречена на шквал критики разной степени конструктивности. IT-сообщество разделилось на три основных лагеря:

  1. те, кто Python боготворят;

  2. те, кто делают вид, как будто его не существует, или о нём практически не слышали;

  3. те, кто ненавидит Питон всем сердцем.

И под влиянием общепринятого человечеством правила, когда на негативное мы обращаем гораздо больше внимания, в этой статье (скорее небольшом эссе из категории «Мысли на тему») я решил обсудить пять основных причин, за которые язык Python ненавидят и критикуют.

Скорость

Наверное, каждый слышал, что Python медленный и уступает в скорости другим языкам по типу С++, С# и т.д.

Отрицать это глупо, однако дьявол, как обычно, в деталях. Python интерпретируемый язык, он по определению не заточен под разработку проектов, специфика которых требует межгалактической скорости. Если используют для других целей и проектов. Там, где это нужно, все узкие, критические для производительности места и библиотеки (например, Numpy) обычно пишут на С или Fortran и оборачивают в питон-код. А в большинстве оставшихся мест это скорость-то особо не нужна или не сильно критична. Кстати, если всё-таки кровь из носу нужно повысить производительность, никто не запрещает использовать другие реализации интерпретатора, например, PyPy, Stackless, Numba и т.д.

К слову, Python далеко не самый медленный язык по сравнению с конкурентами в «своей весовой категории», взять те же PHP и Ruby.

На мой взгляд, судить о крутости и применимости языка исключительно по скорости, это тоже самое, что судить о качестве программы по количеству строчек кода. Разные инструменты, лучше использовать в разных условиях. Если бы краеугольным камнем всего было бы исключительно быстродействие, тогда мы вполне могли бы обойтись ассемблером, а хипстеры, для которых ассемблер казался бы слишком сложным, использовали бы Си.

Ниша Python – это веб, скриптинг и Data Science. Он хорош именно в этих сферах. Если тебе нужны сложные вычисления, CPU-bound задачи, то тебе надо смотреть в сторону свеженького Rust или С/С++. Никто не заставляет писать highload на Питоне. Поэтому попытки ругать Python за скорость схожи с ситуацией, когда ты покупаешь кроссовки на два размера меньше и ругаешь потом фирму за то, что в кроссовках их производства неудобно ходить. Или пытаешься гонять в футбол в свадебных туфлях с носами. Ну вы поняли.

Другая аналогия. Не надо сверлить зуб пациента перфоратором. В смысле, конечно, можно, но будет ли доволен хоть кто-то из вас двоих после этого? Когда инженеры придумывали дрель, они не задумывались, как люди могли бы с помощью неё рыбачить. Продолжать эту цепочку можно довольно долго.

И вы, наверное, удивитесь, что Python обгоняет многие языки программирования по своей скорости, скорости разработки на нём. А это на некоторых проектах гораздо важнее выигрыша в несколько секунд.

Проблемы с многопоточностью и GIL (запрещенная на территории РФ организация)

Как уже оговаривалось раньше, исходя из специфики применения, Python может позволить себе плохо работать с потоками. Кстати, если ты не особо понимаешь, о чем мы сейчас говорим, у меня на YouTube канале есть видео про GIL, рекомендую ознакомиться.

Если тебе обязательно нужно миллион виртуальных потоков, то просто используй Elixir, Erlang или Go. Они с прицелом на это и создавались. Зачем ты ругаешь дуршлаг за то, что вода из него утекает? Ругать надо себя, если ты, будучи «крутым прогером», не способен подобрать подходящий инструмент или перестроиться под особенности задачи.

Допустим, в процессе обработки запроса будет сделано несколько обращений, к API, к базе данных или кэшу. Сервера могут быть сколь угодно быстрыми, но всё упрётся в скорость передачи данных по сети и ожидание ответов от сервисов.  В I/O-bound задачах (на которых Python и применят чаще всего) потоки питона отлично работают, для CPU-bound Python и не предназначен. Кстати, в альтернативу потокам без каких-либо ограничений можно плодить процессы. Да, они заметно тяжеловеснее, однако при грамотном менеджменте и работе с ресурсами можно добиться довольно эффективной работы. Просто надо немного подумать, это вам не просто  библиотеки импортировать)

Низкий порог вхождения

Не сказал бы, что это ужасный минус. Но большинство людей говорят, что Python плодит низкоквалифицированных разработчиков, которые способны только импортить библиотеки, где всё уже сделано за них и всё готово. Знаете, еду из ресторанов, пакетиков, Макдоналдса и другого фастфуда готовить тоже не надо. Всё уже собрано, разложено и пожарено. Надо лишь заказать, сделать импорт, если вам угодно. Только вот почему-то не все люди используют готовую пищу. Многие готовят самостоятельно, зачастую сильно заморачиваются в поисках качественных продуктов, способа приготовления, стараются питаться правильно и экологично. Однако они могут позволить себе съесть фастфуд или заказные блюда в редких случаях, зачастую под какой-то повод или, например, вынуждено перекусить на ходу.

Но есть и те люди, которые едят всё подряд, без разбора, что там да как. Достал, разогрел, сунул в рот и нормально. Такая же история и с программистами.
Ведь ложка не наедается на ночь, ручка не делает орографических ошибок, а пистолет не убивает.
Всё зависит от конкретных людей и того, как они используют окружающие их инструменты.

Тот компетентный человек, который разбирается, например, в нейронных сетях, знает алгоритмы, когда лучше какие применять, понимает, как грамотно подобрать гиперпараметры, с большой долей вероятности сможет самостоятельно написать собственную реализацию любой модели из библиотеки. Только вот зачем ему тратить на это время?  А вот парень, который ничего не понимает, не собирается разбираться, просто насмотрелся видео на YouTube и бездумно импортит модули, так он и программистом работать не сможет, потому что на каждом шагу будет упираться в свою некомпетентность и рано или поздно ему это надоест.

Безусловно, на трушных инженерах и стоит весь мир. Всё, что у нас есть сейчас, мы имеем благодаря хардкорщикам, считавшим в своё время байты на ассемблере и С. Эти люди были, есть и будут востребованы, однако сейчас бизнес также охотно платит и тем, кто может просто качественно выполнять свою работу, собирая софт из фреймворков и модулей, не делая всё с нуля, банально из-за того, что это быстрее, а время сейчас –  это деньги.

Отдельно можно выделить большое количество расплодившихся быдло-кодеров на YouTube (неловко оглядывается в зеркало) и других площадка, которые использует в качестве основы Python. Но поймите, дело тут не в питоне. Python довольно простой язык, поэтому привлекает большое количество народа, и если вдруг, в ближайшее время появится что-то более простое и удобное, эта контингентная прослойка моментально бросит питон. То есть дело, как обычно в самих людях.

При этом из-за низкого порога вхождения, постоянно идёт приток новых разработчиков в комьюнити. За счёт чего оно растёт и развивается. Ведь никому не нравится хочется столкнуться с нечто подобным.

Скриншот сделан 12.04.2021
Скриншот сделан 12.04.2021

Синтаксис

Часто люди не согласны мириться с синтаксисом Python, в особенности с пробелами. Выглядит чужеродно и неудобно. Я некоторое время преподавал программирование в заведениях дополнительного школьного образования.

Давайте сравним, как я пишу код на С++:

И как его пишут дети:

Теперь как я пишу код на Python:

И как его пишут дети:

То-то и оно. К пробелам можно привыкнуть за пару дней, а взамен за моральные страдания, вы получите чётко структурированный, лаконичный, легко читаемый программный код, причём он такой у всех программистов, независимо от их уровня. Кстати, у меня на канале также есть видео о том, как научиться ставить пробелы и табы в Python. Если ты новичок, советую ознакомиться.

А вообще, объективно говоря, на вкус и цвет товарищи разные. Думаю, про синтаксис мы закончили.

Типизация

Это одновременно и плюс, и минус. С одно стороны, безусловно, динамическая типизация тянет проблемы в очень крупных проектах и затрудняет тестирование. С другой же, проще даётся новикам, что обеспечивает приток новой крови в сообщество. Также динамическая типизация дает и несколько других бонусов в виде упрощения метапрограммирования, плюшек интроспекции и рефлексии. Также в Python 3.5 появились аннотации типов – type hinting. Да, это не панацея уровня TypeScript, но указание типов стало довольно приятным нововведением, значительно облегчившим жизнь, и программисты очень часто стали им пользоваться.

Если вы не согласны со мной или вам есть что добавить, то милости прошу в комментарии.

Заключение

Подытожить всё выше сказанное хочется фразой Страуструпа, создателя С++:

«Есть всего два типа языков программирования: те, на которые люди всё время ругаются, и те, которые никто не использует».

И, как я сказал в начале текста:

«Python уже несколько лет держится в топе языков программирования».




Комментарии (107):

  1. menstenebris
    /#22914594 / -4

    — Что, солдат? Ссымся?
    — Так точно. Ссусь.
    — Ну это, солдат, не беда. Такая сегодня экологическая обстановка. Все ссутся. Я ссусь. И даже главком пысается, бывает. Но по ситуации. Что ж нам из-за этого, последний долг Родине не отдавать? Твой позорный недуг мы в подвиг определим, пошлем в десантники. Там ты еще и сраться начнешь.

    (с) ДМБ

  2. bzzz00
    /#22914680 / +5

    «С другой же, проще даётся новикам, что обеспечивает приток новой крови в сообщество.»

    звучит как «сообществу нужны инвалиды»

  3. Sektor2350
    /#22914796 / +6

    Не нравится то как из каждой щели рекламируют курсы от рандом-бокс, где обещают 300к/нано-сек. Не нравятся статьи в которых пишется: n-программа в n-строк (с импортом чуть ли не ядра ОС), без какой либо архитектуры или небольшой структуры, модульности, просто простынки кода (но зато в мало строк!111).
    Типо данный ЯП сразу убирает требования к базовым ИТ-знаниям, Computer Science и т.п. Нет не убирает. ЯП как ЯП, все они инструменты, у Питона есть своя специализация и направленность, а оверхайп, попытка натягивания «совы на глобус» и реклама «вайтишности» языка никак его не красит, вернее она не красит тех кто об этом кричит.
    И в итоге в статье почему-то три гребёнки. Мне не приходится по работе использовать Python, пересаживание на Python также не облегчит те задачи которые я решаю, для текущих моих пэт-проектов он также мало подходит, просто я не использую данный инструмент, мне и с текущими хорошо.

  4. elmirius
    /#22914842 / -2

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

    Ещё одна проблема питона, которую не назвали — отсутствие обратной совместимости у версий 2 и 3.

    • fire_engel
      /#22914898 / +2

      1) а гит, пэйстбин для чего были созданы? Если скрипт с какого-то малоизвестного источника не запускается, тут стоит винить источник, а не ЯП.
      2) зачем нужна эта обратная совместимость, если 2-й питон безнадёжно устарел? Кроме того, развитие питона так же предполагает эволюцию синтаксиса, так что…

      • elmirius
        /#22915196 / +2

        1) На том же медиуме половина примеров кода на питоне нормально не отформатирована. Можно, конечно, обвинять этот «малоизвестный» источник, но примеры других ЯП (те же плюсы, которые упоминал автор поста), скомпилируются нормально даже с поплывшим форматированием.
        2) Нужна затем, что огромное количество кода в интернете написано на втором питоне. Как минимум, чтобы этот код запустить, нужно иметь на компе две версии питона, а чтобы добавить этот код к своему, нужно переписывать. Язык, конечно, скорее всего выиграл от того, что не потащил за собой всё старьё из 2-й версии, но это доставляет определённые неудобства.

  5. AlexLeonov
    /#22914870 / +9

    К слову, Python далеко не самый медленный язык по сравнению с конкурентами в «своей весовой категории», взять те же PHP

    Ничего более смешного за последнее время я не читал. Сравнивать Python по скорости с современным PHP, который давно уже не «интерпретируемый», чья виртуальная машина по скорости уступает нативному коду максимум в 1.5 раза, в котором есть JIT-компиляция… Вы серьезно?

    • KGeist
      /#22919724

      PHP 7+ и в режиме интерпретации быстрее Python'а

      • AlexLeonov
        /#22921172

        Режим интерпретации это php -a в консоли?

  6. ptr128
    /#22914902 / +6

    Не думаю, что кто-то ненавидит Python. Просто низкий порог вхождения привёл к наличию тех, кто Python боготворит фанатически. IMHO, если у кого-то ненависть и возникает, то не к Python, а к тем, кто кроме него ни одного языка не знает, но не только пытается применять Python везде где ни попадя, а еще и искренне считая, что знание Python достаточно для решения любой задачи.

    Иными словами, ненавидят не Python, а некоторую часть фанатиков Python, настаивающих на его использовании в качестве «золотого молотка» для решения любых задач.

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

    • 0xd34df00d
      /#22915034

      Ненависть — вообще слишком сильное чувство. Я не люблю питон, потому что мне сложно на нем писать. Вот когда с ним таки приходится встречаться (вспомогательный скрипт там допилить, что-то такое), тогда уже появляются более сильные чувства.

      • ptr128
        /#22915098

        Все познается в сравнении. Мне приходилось продуктивные(!) системы писать на COBOL и ассемблере IBM/370. Потом на сопровождение принял программный комплекс на Lisp. REXX после этого воспринимался, как дар божий, а Perl — как верх совершенства. Что уж тут говорить про Python?
        После того, как пришлось покодировать за свою жизнь на десятке-другом языках (T-SQL, PL/SQL и PL/pgsql считаю разными языками, хоть они и похожи), толерантность резко повысилась и сложные в освоении языки больше не попадаются )

        • 0xd34df00d
          /#22915130 / +1

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

          • ptr128
            /#22915260 / +1

            «Неприятно» — субъективное понятие. А если объективная реальность требует использования конкретного языка — это уже обязанность разработчика. Нет такой работы, где не приходилось бы заниматься «неприятными» (чаще — нудными и рутинными) вещами. Рекомендую даже не думать о том, насколько работа неприятна. Лучше в любой задаче находить что-то позитивное. И тогда все станет проще и легче.

            Например, в свое время охреневал от DAX, потом от R. Ничего, зато освоил и обнаружил, что некоторые задачи на DAX решаются эффективней, чем на MDX, а на R — эффективней, чем на Python.

            • 0xd34df00d
              /#22916138 / -2

              Поэтому я стараюсь выбирать работу, в том числе, по стеку.


              эффективней

              Моя эффективность на питоне после строк 50-100 стремится к нулю. Я начинаю пугаться отсутствия типов и звать маму.

              • Andy_U
                /#22916624

                Если использовать PyCharm (не знаю, есть ли еще что-нибудь подобное) и, дополнительно, type hints и форматированные doc-strings, то жизнь сильно облегчается.

                • deitry
                  /#22916926

                  В VS Code достаточно неплохая поддержка автодополнения (с учётом типов, естессно), линтеров/форматтеров и прочих радостей жизни; однако последний раз когда смотрел, не было рефакторингов за исключением ренейма. PyCharm'ом не пользовался, но уверен, что там с этим лучше.

                • 0xd34df00d
                  /#22918872

                  А потом даже во включенных батарейках нет указания типов (как, например, в функции паркинга жсона, по крайней мере, пару лет назад), и приплыли.

                  • Andy_U
                    /#22919068

                    А можно конкретный пример? Хотя я и сам могу в пример matplotlib привести. Но тут возраст пакета почтенный, да он, небось, еще и совместимость с Python 2.X поддерживает.


                    Ну и кстати, что, в С/С++ не бывает такого:


                    func(float a, float b, ... float z)

                    P.S. На фортране можно писать на любом языке.

                    • 0xd34df00d
                      /#22919252

                      А можно конкретный пример?

                      Так это, функция парсинга жсона. Чтобы понять, что она возвращает, нужно было её запускать с разными входными данными (строкой, объектом, массивом, и так далее). Более того, если в каком-то другом языке я могу просто описать тип данных, который я ожидаю, и библиотека вместе с компилятором за меня сгенерит всю сериализацию-десериализацию, то после этого я могу быть уверен, что если жсон распарсился корректно, то там именно нужные мне данные в нужном мне формате. В питоне, говорят, так тоже можно, но IDE от этого срывает голову.


                      Ну и кстати, что, в С/С++ не бывает такого:

                      Бывает, конечно. Но если вы сравниваете питон с худшим случаем для плюсов, которые сами по себе далеко не предел мечтаний с точки зрения системы типов, то это, наверное, о чём-то говорит.

                      • Andy_U
                        /#22919386

                        По поводу json'а — не понимаю проблемы. В описании пакета json есть ведь таблица конвертации?


                        В питоне, говорят, так тоже можно, но IDE от этого срывает голову.

                        Можно коротенький пример?


                        библиотека вместе с компилятором за меня сгенерит всю сериализацию-десериализацию,

                        А вам, часом, не pickle нужен? Он как раз за это отвечает.


                        Но если вы сравниваете питон с худшим случаем для плюсов,

                        Так и вы, возможно (пример?) не лучший случай для питона рассматриваете.

                        • 0xd34df00d
                          /#22919504

                          По поводу json'а — не понимаю проблемы. В описании пакета json есть ведь таблица конвертации?

                          То есть, вместо того, чтобы просто посмотреть на типы, где это вполне могло бы быть выражено, мне искать таблицы конвертации?


                          Можно коротенький пример?

                          Сейчас уже не воспроизведу, к сожалению (особенно с гарантией глюков IDE — для этого надо как минимум будет поставить pycharm). То пару лет назад было, с тех пор с питоном особо не встречался.


                          А вам, часом, не pickle нужен? Он как раз за это отвечает.

                          Нет. Если формат жсона зафиксирован, то бывает удобно определить под него какой-то конкретный тип данных, в который бы этот жсон бы парсился.


                          Так и вы, возможно (пример?) не лучший случай для питона рассматриваете.

                          А в каком примере отсутствие типов будет полезно?

                          • menstenebris
                            /#22919660

                            Нет. Если формат жсона зафиксирован, то бывает удобно определить под него какой-то конкретный тип данных, в который бы этот жсон бы парсился.

                            pydantic-docs.helpmanual.io

                            • Andy_U
                              /#22920168

                              А с результатом json.loads() -> List[Any] это сработает?

                            • Andy_U
                              /#22920728

                              Вдогонку — был неправ: Сигнатура json.loads из typeshed


                              def loads(
                                  s: Union[Text, bytes],
                                  encoding: Any = ...,
                                  cls: Optional[Type[JSONDecoder]] = ...,
                                  object_hook: Optional[Callable[[Dict[Any, Any]], Any]] = ...,
                                  parse_float: Optional[Callable[[str], Any]] = ...,
                                  parse_int: Optional[Callable[[str], Any]] = ...,
                                  parse_constant: Optional[Callable[[str], Any]] = ...,
                                  object_pairs_hook: Optional[Callable[[List[Tuple[Any, Any]]], Any]] = ...,
                                  **kwds: Any,
                              ) -> Any: ...
                              

                          • Andy_U
                            /#22920162

                            То есть, вместо того, чтобы просто посмотреть на типы, где это вполне могло бы быть выражено, мне искать таблицы конвертации?

                            Что их искать — в официальной документации по питону прочитать про пакет json? Ну или пройдите по списку, который возвращает json.loads(), напечатайте типы элементов. Если тип не скалярный — рекурсивно для элемента. Или в режиме отладки посмотрите, что там функция вернула. Это если json'ы однотипные. Ну а как иначе, если функция, вообще говоря, читает произвольный валидный json-файл?


                            Сейчас уже не воспроизведу, к сожалению (особенно с гарантией глюков IDE — для этого надо как минимум будет поставить pycharm).

                            Ну, да, вот в коде ниже mypy ошибку видит, а последний Pycharm — нет (и он еще считает, что тип полей Any). Надо будет issue создать (если уже не создано). Но это совсем не "у IDE от этого срывает голову." False negative — все-таки лучше false positive. Ну и все равно надо периодически mypy использовать.


                            Pycharm - false negative
                            import json
                            
                            def f(x: bool) -> None:
                                print(isinstance(x, bool), type(x), x)
                            
                            class Entity:
                            
                                def __init__(self, data: str):
                                    self._a: int
                                    self._b: bool
                                    self._a, self._b = json.loads(data)
                            
                                def ff(self) -> None:
                                    f(self._a)  # <- mypy error: main_mypy.py:16: error: Argument 1 to "f" has incompatible type "int"; expected "bool"
                            
                            def main() -> None:
                            
                                e = Entity('[1, true]')
                                e.ff()
                            
                            if __name__ == '__main__':
                                main()

                            • 0xd34df00d
                              /#22920228

                              Что их искать — в официальной документации по питону прочитать про пакет json?

                              Когда я читал доки на json.load(s) пару лет назад — ссылки на conversion table не было, честно-честно. Или я читал какие-то не те доки. Впрочем, вот у меня на одной из машин сохранился python 3.7, давайте


                              почитаем
                              >>> help(json.load)
                              Help on function load in module json:
                              
                              load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
                                  Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
                                  a JSON document) to a Python object.
                              
                                  ``object_hook`` is an optional function that will be called with the
                                  result of any object literal decode (a ``dict``). The return value of
                                  ``object_hook`` will be used instead of the ``dict``. This feature
                                  can be used to implement custom decoders (e.g. JSON-RPC class hinting).
                              
                                  ``object_pairs_hook`` is an optional function that will be called with the
                                  result of any object literal decoded with an ordered list of pairs.  The
                                  return value of ``object_pairs_hook`` will be used instead of the ``dict``.
                                  This feature can be used to implement custom decoders that rely on the
                                  order that the key and value pairs are decoded (for example,
                                  collections.OrderedDict will remember the order of insertion). If
                                  ``object_hook`` is also defined, the ``object_pairs_hook`` takes priority.
                              
                                  To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
                                  kwarg; otherwise ``JSONDecoder`` is used.

                              • Andy_U
                                /#22921500

                                Говоря про документацию, я имел ввиду вот это, или offline вариант, который ставится вместе с питоном. Ну, да, type hints сюда еще не завезли, как и много еще куда. Но хотя бы написано, что "If the data being deserialized is not a valid JSON document, a JSONDecodeError will be raised". Кстати — там и translation table есть. Т.е. тип результата loads Any можно заменить было бы на Union[int, float, str, bool, None, List, Dict]. А нет, можно и нестандартные типы на выходе иметь, см. пример. Таки Any.


                                Но есть вот такой продукт (его, кстати, тот же PyCharm для подсказок и проверки типов использует), куда завезли. Вот как там выглядит


                                сигнатура json.loads
                                def loads(
                                    s: Union[Text, bytes],
                                    encoding: Any = ...,
                                    cls: Optional[Type[JSONDecoder]] = ...,
                                    object_hook: Optional[Callable[[Dict[Any, Any]], Any]] = ...,
                                    parse_float: Optional[Callable[[str], Any]] = ...,
                                    parse_int: Optional[Callable[[str], Any]] = ...,
                                    parse_constant: Optional[Callable[[str], Any]] = ...,
                                    object_pairs_hook: Optional[Callable[[List[Tuple[Any, Any]]], Any]] = ...,
                                    **kwds: Any,
                                ) -> Any: ...

                                • 0xd34df00d
                                  /#22923100

                                  Говоря про документацию, я имел ввиду вот это, или offline вариант, который ставится вместе с питоном.

                                  Я когда-то давно слышал, что питон хорош тем, что можно в репле написать help(foo) и не тянуться к браузеру.


                                  Это какой?

                                  Хаскель.


                                  Я правильно понял, в командной строке (интерпретатора)? Да ну, в Pycharm для этого надо консоль открывать. Проще "нормальный" help открыть.

                                  Так репл же и так всегда рядом открыт (у меня, по крайней мере).


                                  В питоне — на входе или строка или байты. Вроде, и достаточно?

                                  Так вход мы вообще не обсуждаем, там всё проще.


                                  Т.е., фактически, тот же Any.

                                  Нет. Тут принципиальная разница в том, кто задаёт тип возвращаемого значения. Any — это вызываемая функция выбирает, какой тип вернуть, и заворачивает его в Any (типотеоретик скажет, что это экзистенциальный тип). В примере выше возвращаемый тип выбирается вызывающей функцией.


                                  Я могу явно написать decode myStr :: Maybe [Int], чтобы попытаться распарсить жсон как список интов, или decode myStr :: Maybe Person, чтобы функция decode пыталась распарсить жсон как какой-то мой тип Person.


                                  Все равно — проверка на валидность в runtime?

                                  Проверка на валидность чего, жсона? Да, конечно в рантайме, иначе-то как с поступающими в рантайме данными.


                                  Но эта проверка делается один раз, внутри функции decode, после неё вы можете быть уверены, что если функция вернула не ошибку, то там корректные данные, и ничего в рантайме проверять не нужно.


                                  Ладно, проверили, но дальше, все равно придется в runtime же разбираться, что там конкретно внутри древообразное живет?

                                  Поэтому при прочих равных надо стараться изебгать Value и парсить во что-то более предметноспецифичное.


                                  Что может быть в узлах у питона, см. ту же translation table. Плюс custom types (см.выше).

                                  Проблема в том, что эту translation table могу прочитать я как человек, но не может прочитать машина, поэтому для неё нужны какие-то отдельные тайпхинты или что-то такое (и которую не запилили).


                                  Наоборот. Не генерирует ошибку там, где она есть.

                                  Тогда я с вами категорически, философски не согласен. Гораздо лучше, когда ошибка генерируется, даже если её нет, чем если она не генерируется, когда она на самом деле есть. В последнем случае в прод может уйти какая-то ерунда, а в первом случае достаточно убедить тайпчекер, что он неправ.

                                  • Andy_U
                                    /#22923842

                                    Так вход мы вообще не обсуждаем, там всё проще.

                                    Вы же написали ранее, что decode:


                                    … может использоваться для того, чтобы распарсить любой тип, реализующий FromJSON

                                    или это валидатор?


                                    Any — это вызываемая функция выбирает, какой тип вернуть, и заворачивает его в Any

                                    Это не тип, а type hint "на отвяжись", чтобы IDE не ругался на отсутствие типа. Т.е. функция может просто вернуть любой реальный тип.


                                    Я могу явно написать decode myStr :: Maybe [Int], чтобы попытаться распарсить жсон как список интов, или decode myStr :: Maybe Person, чтобы функция decode пыталась распарсить жсон как какой-то мой тип Person.

                                    Не могу не согласиться — удобно. В питоне придется воспользоваться внешним пакетом jsonscheme (см. также https://json-schema.org/) или чем-то подобным и написать свое расширение для json.loads().


                                    Но эта проверка делается один раз, внутри функции decode, после неё вы можете быть уверены, что если функция вернула не ошибку, то там корректные данные, и ничего в рантайме проверять не нужно.

                                    Не понимаю, как ваш haskel работает. Из вашего последнего примера в предыдущем сообщении видно, что если в runtime на вход decode подать строку (из файла, несколько, разных), то она их будет каждый раз в runtime проверять и парсить.


                                    Проблема в том, что эту translation table могу прочитать я как человек, но не может прочитать машина, поэтому для неё нужны какие-то отдельные тайпхинты или что-то такое (и которую не запилили).

                                    Она как раз для человека нужна, чтобы, посмотрев на конкретный пример json-файла, написать парсер результата работы json.loads() в нужный формат. Типа выдернуть лишь часть полей? Раскидать по именованным атрибутам класса и т.д. и т.п.

                                    • 0xd34df00d
                                      /#22927298

                                      Вы же написали ранее, что decode:
                                      … может использоваться для того, чтобы распарсить любой тип, реализующий FromJSON

                                      Эм, ну да. Ему на вход приходит строка с жсоном. На выходе — либо значение какого-то типа (который задаётся вызывающей стороной), либо ошибка.


                                      или это валидатор?

                                      Наверное, можно сказать, что это парсер и валидатор.


                                      Из вашего последнего примера в предыдущем сообщении видно, что если в runtime на вход decode подать строку (из файла, несколько, разных), то она их будет каждый раз в runtime проверять и парсить.

                                      Да, но проверка будет только внутри decode. После того, как она вам вернула Just someValue вместо Nothing, проверять, что внутри someValue не нужно, это уже статически гарантируется типами.

                              • Inspector-Due
                                /#22921920

                                Действительно, мало чего можно понять по описанию json.load. Мы не знаем ни про типы, которые он может вернуть, ни про исключентя, которые он может выбросить. Так что единственный нормальный способ, как по мне, это попробовать функцию в REPL. Можно посмотреть на поведение при разных входных данных. Тогда станет более менее понятно.
                                Тогда и стиль программирования отличается. Если в том же Haskell мы можем рассмотреть все возможные варианты и обработать все возможные ошибки, а в Python об ошибках мы будем узнавать уже во время разработки (т.е. забудем какую-то вещь обработать, но только через время о ней узнаем) и даже в продакшене (есть такая вещь, как sentry). Возможно, проблема в стиле. Вы привыкли программировать в одном стиле, и оказывается, что на Python так программировать будет тяжело.


                                Кстати, что там с количество библиотек под Haskell и как там с его популярностью? Я понимаю, что популярность не должна играть большую роль, но ведь от популярности в какой-то мере зависит и количество библиотек. Чем больше библилиотек, тем больший спектр задач можно решать на Haskell. Другой вопрос — а нужна ли такая гибкость?

                                • 0xd34df00d
                                  /#22923114

                                  Кстати, что там с количество библиотек под Haskell и как там с его популярностью?

                                  Библиотеки для всех вещей, которые ожидаешь найти, находятся (от того же парсинга жсонов до, не знаю, линейной алгебры, построения графиков, веб-фреймворков, DSL'ей для программирования GPU и библиотек, помогающих описывать оптимизации для самодельных компиляторов).


                                  Популярность — ну, в TIOBE не в топчике, это да, приходится с этим как-то жить.

                                  • unsignedchar
                                    /#22924620

                                    Если взял в руки молоток, начинает казаться, что забить им можно буквально всё. И что все прочие инструменты не такие удобные ;)

                                    • 0xd34df00d
                                      /#22927304

                                      Ну что вы, для ряда задач есть ряд инструментов куда удобнее хаскеля! Верифицированный код я бы на нём писать не стал бы, например.

        • anasana
          /#22916770

          Мне хорошо за сороковник, на разных писал и пишу в разное время. И сейчас понял, что кода именно на питоне последнее время стало встречаться намного больше. Стало обидно, что проходил мимо него столько времени. Накачал pdf-ok, когда валялся с модной болезнью, купил книжку, сейчас кайфую что понимаю о чем речь в кусках программ, хотя первых два дня ловил себя на мысли, что внутренне возмущался когда видел некоторые языковые конструкции питона и пыхтел, ведь в ХХХ и УУУ это же сделано совсем иначе :). Зато в копилочке у меня появилась возможность запускать и понимать внутреннюю кухню в проектах с интересными мне направлениями. Зачем сознательно сейчас лишать себя еще одного инструмента.

          • 0xd34df00d
            /#22918884

            Потому что есть инструменты лучше. Те же прототипы или мелкие скрипты я пишу на хаскеле.


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

            • Andy_U
              /#22919248

              Бывают еще ситуации, когда, кроме питона и нет ничего больше. Я про embedded interpreter. Хочешь автоматизировать параметризованное построение геометрии и вычислительной сетки — вот тебе АPI в виде модуля, который с потрохами программы двунаправленно общается, и вперед. А у меня эта программа (версии, естественно, разные, недавно даже третий питон завезли. один из двух основных рабочих инструментов вот уже лет 15 наверное. Вторая — малоизвестный FEM solver. Я так и забросил C++. Сначала я на нем всякий постпроцессинг, в частности, писал, а потом попробовал на питоне. Код оказался раз в 10 короче. Спасибо пакетам numpy/scipy/matplotlib. А то, что разовая программа отрабатывает не за 10 секунд, а за пару минут, так и что?


              Ну и питон есть у всех в округе. На ноутбуках, рабочих станциях, кластерах, виртуалках...

              • 0xd34df00d
                /#22919262

                Я так и забросил C++. Сначала я на нем всякий постпроцессинг, в частности, писал, а потом попробовал на питоне. Код оказался раз в 10 короче. Спасибо пакетам numpy/scipy/matplotlib.

                А на плюсах вы чем пользовались? Какой-нибудь там dlib или eigen?


                А то, что разовая программа отрабатывает не за 10 секунд, а за пару минут, так и что?

                Если она действительно разовая, то и нормально. Но программы ж не всегда разовые.

                • Andy_U
                  /#22919458

                  А на плюсах вы чем пользовались? Какой-нибудь там dlib или eigen?

                  Не помню я их в то время. BLAS, LAPACK… Опять же, на Linux-серверах они были, а собирать что-нибудь серьезное без административных прав — нафиг. Я помню, как матерился, собирая себе в таком режиме midnight соmmander.


                  Если она действительно разовая, то и нормально. Но программы ж не всегда разовые.

                  А когда отрабатывает на кластере в пакетном режиме после суточного расчета, тоже нормально.

                  • 0xd34df00d
                    /#22919506

                    Не помню я их в то время. BLAS, LAPACK…

                    Голый блас или лапак — это очень больно, да. Обёртки вроде dlib/eigen сильно повышают юзабельность (и увеличивают производительность за счёт espression templates).


                    А когда отрабатывает на кластере в пакетном режиме после суточного расчета, тоже нормально.

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

                    • Andy_U
                      /#22919528

                      А если сутки удаётся свести до нескольких минут

                      Расчет, скажем, в Fluent?

                      • 0xd34df00d
                        /#22920134

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

                        • Andy_U
                          /#22920180

                          Ну, это понятно, что совсем другая история.

  7. victoriously
    /#22914984 / +1

    К пробелам можно привыкнуть за пару дней, а взамен за моральные страдания

    Один таб — один уровень вложенности. Логично же.


    Как по мне, так это плюс. Это отучает людей писать код как на втором скриншоте с++, так и отсекает тех, кто хочет писать все в одну строчку.

    • Nehc
      /#22917584

      Ну… в одну строчку-то как раз вроде можно, не? ;) Проблема с отступами от двух и выше…

      • evgenyk
        /#22919030

        Вот это тоже хорошо, ограничивает любителей накрутить вложенных условий и циклов.

  8. DMGarikk
    /#22915002 / +8

    ну по поводу форматирования, сейчас когда в ide можно выделить кусок неформатированного кода и нажать refactor… какоето натянутое «приемущество»

    а вот динамическая типизация это реально боль… как только проект переваливает какойто размер (и это далеко не «огромный») сразу начинаются костылестроения по контролю типов

  9. Shtucer
    /#22915018 / +1

    def get_city(city):

    Добавим сахарку детишкам.


    def get_city(city: str) -> str:

  10. deitry
    /#22915026 / +1

    Не боготворю, но люблю питон за скорость отладки и обилие тулзовин. Возникла потребность проверить какую-нибудь математику или алгоритм, раз-два — и уже есть прототип, юнит-тесты с помощью pytest, и проверка типов от mypy, и всё это причёсывается yapf-ом.


    Код на C++… и как его пишут дети

    Вот вам смешно, а у нас так 80% проекта выглядит. Не совсем так, конечно, и C# вместо плюсов, и не выиграл, а проиграл, но очень похоже.

  11. acklamterrace
    /#22915054 / +7

    Забавно, что самому главному недостатку Python отведен маленький абзац в самом конце. "Ну нет типизации, никто не знает, где и когда оно рванет в продакшне, зато новичкам проще писать hello world"

    • DMGarikk
      /#22915094 / +3

      забавно что фанаты питона реально не считают это недостатком

      помнится на заре своего увлечения программированием я увлекался VB6… и у него главным недостатком считался тип Variant и довольно вольный контроль типов (без option explicit)… потом шла его сущность в виде полу-интерпретатора… типа фуу… это не язык а черти что
      прошло 20 лет… и буквально все минусы VB6 я слышу от фанов питона как неоспоримые плюсы… вот уж времена меняются

      • evgenyk
        /#22915154 / -3

        В питоне вообще-то строгая типизация. Но динамическая. И это очень круто.

        • 0xd34df00d
          /#22916148

          Динамическая типизация — это


          устоявшийся оксюморон


          [...]


          // из Types and Programming Languages

    • evgenyk
      /#22915152 / -2

      Это не недостаток.

      • acklamterrace
        /#22915306 / +2

        Опять же, зависит от задач. В jupyter notebook или простеньких скриптиках, или для обучения конечно неохота возиться с типами и ругаться с компилятором. Вот пусть python там и остается :-)


        А в продакшне должен быть тоталитарный фашизм по типу rust, тогда может быть писать код придется немного медленнее, но зато можно будет спокойно (спокойнее) спать по ночам.

    • deitry
      /#22915334

      Вообще как раз сейчас (где-то 3.6+) с типизацией в питоне всё лучше и лучше. Есть аннотации типов и способы контроля (см. mypy), минимальный набор потребностей покрывает.


      В больших проектах и/или со сложными структурами данных может быть всё что угодно, но это повод задуматься, а правильным ли микроскопом забиваешь гвозди.

      • 0xd34df00d
        /#22916156 / +2

        Есть аннотации типов и способы контроля (см. mypy), минимальный набор потребностей покрывает.

        Ну, может, года так для 1970-го — да. mypy по выразительной силе недалеко ушла от plain C (по крайней мере, когда мне приходилось с ней сталкиваться пару лет назад).

        • menstenebris
          /#22916990 / +1

          mypy активно развивается. там сейчас даже протоколы (аналог типажей из rust и scala) есть, корявые но есть. Но основная фишка которая в разработке это mypyc. вот неплохая статья habr.com/ru/company/exness/blog/542106

          • 0xd34df00d
            /#22918886

            Ну вот типажи корявые, дженерики корявые, получается в итоге коряво и непонятно, зачем, если можно взять изначально типизированный язык (при прочих равных, конечно же).

  12. Alexey2005
    /#22915416 / +2

    Первая и самая главная претензия к Python — постоянные и неизбежные проблемы с зависимостями. Проекты сложнее Hello World никогда не заводятся с первого раза. Попытайтесь, к примеру, запустить GPT-3 от Сбербанка на вашей домашней машине, и вы тоже начнёте не любить Python, потому что вам придётся упороть много часов, разбираясь, почему программа падает, когда вроде всё из requirements поставлено. Причём падает молча, тупо крэшем, даже без стектрейса… Хуже с зависимостями работает разве только C++.
    Вторую претензию уже упоминали — использовать форматирование в качестве элемента синтаксиса видимо придумали специально чтобы затруднить копипасту так сильно, как только возможно. При копировании кода все эти Tab'ы с пробелами гарантированно поплывут.
    Третье — совершенно кошмарная работа Python со строками. Стоит только во входных данных попасться «неправильному» символу, и всё повалится с километровым стектрейсом, увенчанным UnicodeDecodeError. Причём, конечно же, упадёт совсем не там и не тогда, где эта строка возникает, после чего нас ждут увлекательные часы отладки.
    И это при том, что практически во всех данных, которые получаются из внешних источников, гарантированно будут «неправильные» символы, не соответствующие стандарту Unicode. А если кодировки гоняются туда-сюда (скажем, надо отдавать текст не в utf-8, а в cp1251), то всё становится совсем печально. Ни один другой язык не спотыкается таким кошмарным образом тупо на обработке символов. Оно ж от любого чиха валится…
    А если такой символ попадёт в исходники (например, в комментарий), то вообще труба. Оно начнёт валиться в произвольных местах, и вы даже не сразу поймёте, в чём вообще проблема.

    • unsignedchar
      /#22915752

      А как должна вести себя программа, словившая unicode error, но не оборудованная проверкой на ошибку? Всё равно упадёт, только немного позже. Что и произошло.

      • fcoder
        /#22916502

        не скомпилироваться, например, ввиду отсутствия гарантии правильности данных поступающих на обработку

        • Andy_U
          /#22916688

          Ну, Питон это, все-таки, интерпретатор...

        • unsignedchar
          /#22917424

          Программа на С++ не скомпилируется, если ей на вход подадут когда-нибудь не те данные? Круто, конечно :)

          • fcoder
            /#22924096

            Если в программе (на любом языке) принимается допущение что любой сырой набор байтов — это валидная UTF-последовательность, то это ошибка в логике.


            Язык С++ перекладывает заботу о валидации на программиста. Поэтому если программист "забыл" добавить валидацию, случается краш.


            В языке Раст например, устроено так что "забыть" невозможно. Необходимо явно написать каким именно образом преобразовать сырые данные в utf8 — from_utf8 (и обрабатывать Result), from_utf8_lossy (который вставит плейсхолдер в места, которые сломаны) или как-то ещё

      • Alexey2005
        /#22917710

        Есть два основных варианта поведения. Первый — это когда строка обрабатывается просто как набор байт по принципу «Garbage in — Garbage out». И если там на входе встречается неправильный символ, то и на выходе будет такой же, а остаток строки нормально обработается. Этот подход обычно используют C/C++ библиотеки.
        Второй вариант — автоматически заменять битые символы плейсхолдером U+FFFD (REPLACEMENT_CHARACTER) — "?". При этом строка также успешно обработается, и если есть желание, то после обработки можно даже найти индексы неправильных символов.
        А вот как эту задачу решить в Python? Вот есть строка. Вот мы точно знаем, что где-то в ней имеются битые символы. Как их найти и убрать, если при любой попытке это обработать программа сразу упадёт? Это не очень-то тривиальная задача, тогда как для большинства остальных языков это вообще стандартное поведение — обрабатывать строки «как есть».

        • Andy_U
          /#22917790

          Открывать файл с помощью codecs.open(), где можно определить политику обработки ошибок, или ручками — читать в бинарном виде, потом декодировать: bytes.decode().


          Вдогонку, да обычный open это умеет…

        • unsignedchar
          /#22917830

          "А вот как эту задачу решить в Python?"
          Как и везде. Ловить и анализировать exception.


          "для большинства остальных языков это вообще стандартное поведение — обрабатывать строки «как есть»."


          Дык в python объект типа string (с unicode в нутре) немного сложнее устроен, чем char* :)

    • Andy_U
      /#22916824

      А если такой символ попадёт в исходники (например, в комментарий), то вообще труба. Оно начнёт валиться в произвольных местах, и вы даже не сразу поймёте, в чём вообще проблема.

      Да даже если валидный: https://bugs.python.org/issue38755.

  13. mSnus
    /#22916194

    Python вполне хорош для своих задач. Например, как shell script. Или для ученых, которым нужны хорошие графики, анализ данных без особых затрат.


    Плох он становится там, где им пытаются заткнуть задачи в неподходящих местах. Например, если "программист Python" уверен, что любая задача должна решаться через "pip3 install", и для простого сайта из 5 страничек заводит Django. А тот падает.


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


    В общем, боль есть, но до ненависти ей далеко.

  14. Stas911
    /#22916298

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

  15. hellamps
    /#22916364

    Не осилил питон прямо с синтаксиса и пробелов. Может пусть детишки и дальше пишут? И эти датасайентологи

  16. thegriglat
    /#22916764

    Современный c++ по моему опыту весьма близок к питону по скорости разработки — auto, лямбды в помощь. "Проблема" с пробелами элементарно решается файлом .clang-format в репозитории (конечно, форматировать Легаси тот ещё ад для git)
    Для пакетов — Conan


    Раньше программировал больше на питоне, потом полностью перешёл на плюсы, и не жалею — спокойный сон за типы дороже

    • PyLounge
      /#22916768

      Соглашусь, 11 и 14 стандарты заметно облегчили жизнь

    • Alexey2005
      /#22917676

      По моему опыту, скорость разработки на 95% определяется не синтаксическим сахаром или там сборкой мусора, а скоростью поиска и интеграции чужого кода. В конце концов, самый быстро разрабатываемый код — это тот, который уже кем-то написан.
      И вот тут C++ в хлам проигрывает Python'у. Если в стандартной библиотеке Python есть практически всё, на что только у вас хватит фантазии (а чего нет — то находится в шаговой доступности в виде pip), то в C++ всё куда печальнее. Стандартная библиотека C++ на удивление куцая, там даже нормальной работы с сетью до сих пор нет (это в 2021 году, когда всё давно в облака ушло!).
      Вот например, нужно просто скачать файл с какого-то URL для последующей обработки. В Python скачать файл можно в две строки, а в C++ вам придётся искать сторонние библиотеки и как-то их подключать/собирать. Это будет куда медленнее, и никакие лямбды не компенсируют потерю скорости.
      Хотя, если вы пишете программы только под Windows, то тут всё неплохо — WINAPI могут буквально всё, и есть сразу «из коробки».

      • DMGarikk
        /#22917712

        вы pip как панацею не превозносите.
        каждую 'офигенную ' либу оттуда надо по возможности перепроверять и смотреть что пишут в репозитории, в issues и в комментах да и в код заглянуть желательно

        Я уже натыкался несколько раз на проекты в которые приходил, когда 'ребяяят… крындец, django обновился до 3.12 и всё умерло'… а умерли такие универсальные либы у которых last commit на гитхабе 5 years ago несмотря на пару сотен звездочек
        также бывает в коде либ такая страшная дичь написана что проще самому переписать с нуля чем бездумно их юзать.

      • unsignedchar
        /#22917874

        "Стандартная библиотека C++ на удивление куцая"


        На то она и стандартная. Для С++ есть boost, кстати.


        "там даже нормальной работы с сетью до сих пор нет"


        Учитывая, что работа с сетью — это огромный платформозависимый кусок кода — наверное оно и правильно.

      • hellamps
        /#22920164

        так для этого придумали ноду и забыли про питон

      • thegriglat
        /#22929912

        со временем собирается личная копилка «fast-develop» рецептов, например класс-хелпер для cURL, для json тоже полно хидерных библиотек (https://github.com/nlohmann/json как наверное самая user-friendly)

        пишу исключительно под Linux, работа c сетью, JSON и REST API (под капотом числодробилка, python не пойдет, разница 5мин vs 10 часов)

        С питоном уже надоело возиться с импортом под разные версии (код часто запускается в разных окружениях, то python2.x, то python3.x) и несовместимостью 2vs3

        Логика stdlib c++ такова уж, сеть это не std, хотя вот смотря на прогресс C++20 может скоро и появится (но зачем, если есть cURL?)

  17. Emelian
    /#22917082

    Как по мне, то Питон – супер язык программирования! Но только в своей области применения. Пока мне его более чем достаточно для задач одноразовой подготовки (обработки) данных. Для этих целей он просто идеален!

    Для программирования других задач мне очень нравится C++ / WTL. Но как в С++, так и в Питоне, мне совершенно не нравиться стиль разметки кода, практикуемый большинством. Конкретно в Питоне, я всегда делаю отступ состоящий из двух пробелов. В качестве «закрывающего» тега использую комментарий, типа:

    #==============================================================================
    # exec(open('Html.py', encoding='utf-8').read())
    # Html.py – Извлечение данных из html-страницы
    #==============================================================================

    #==============================================================================
    # Глобальные переменные (только на чтение, при изменении используется модификатор 'global')
    #==============================================================================

    # Имя файла данных
    _SrcName = '661830' # Слово из онлайнового словаря, с максимально объемным описанием

    #...

    #==============================================================================
    # HtmlParsing2() — Очистить результаты предварительной обработки html-страницы от пустых строк и прочего «мусора»
    #==============================================================================
    def HtmlParsing2(OutFile, Html):
    # Максимальная позиция строки в списке Html
    MaxPos = len(Html) — 1
    NewBlock = False

    for i in range(MaxPos):
    HtmlLine = Html[i].replace(' ', '').strip()

    # Удалить ненужные символы из html-строки
    HtmlLine = DeleteSpam(HtmlLine, NewBlock)

    if(len(HtmlLine) > 0):
    OutFile.write(HtmlLine)
    NewBlock = True
    # if(len(HtmlLine) > 0)
    # for i in range(maxPos)

    return
    # HtmlParsing2()

    #...

    #==============================================================================
    # Main() – Главная функция
    #==============================================================================
    def Main():
    # Обработка данных
    Processing()

    # Завершение работы
    return
    # Main()

    if __name__ == "__main__": Main()

    #==============================================================================
    #==============================================================================

    Так что, любой опенсорсный код, используемый в проекте приходится переформатировать «под себя».

    P.S. Отступы здесь теряются, при вставке кода.

  18. saboteur_kiev
    /#22917210

    те, кто Python боготворят;
    те, кто делают вид, как будто его не существует, или о нём практически не слышали;
    те, кто ненавидит Питон всем сердцем.

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

    Наверное, каждый слышал, что Python медленный и уступает в скорости другим языкам по типу С++, С# и т.д.

    Скорость почти всех языков программирования зачастую упирается не в скорость самого языка, а в реализацию нужных функций библиотеками этого языка. Я вот не слышал, что питон медленный, что я делаю не так?

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

    Мне даже интересно, автор пробовал на ассемблере обойти по скорости современный Си? И много ли найдется в мире людей, которые смогут это сделать?

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

    • DMGarikk
      /#22917616

      Я вот не слышал, что питон медленный, что я делаю не так?

      вы не берете те задачи в которых производительность упирается в питон

      • saboteur_kiev
        /#22921876

        Ну так надо тогда указать, что в определенных задачах питон медленный, а не «он вообще медленный». Потому что есть множество задач, где такой вопрос даже не поднимается.

    • 0xd34df00d
      /#22918902

      Мне даже интересно, автор пробовал на ассемблере обойти по скорости современный Си? И много ли найдется в мире людей, которые смогут это сделать?

      Если алгоритм достаточно мелкий, чтобы реализовать его на ассемблере в разумные сроки, то да, это делается легко, иногда с профитом в 3-10 раз.

      • unsignedchar
        /#22919784

        Это если так совпало, что алгоритм мелкий, и настолько критичный по времени выполнения, что ради этого имеет смысл реализации на ассемблере. Какие нибудь матричные операции на SIMD регистрах, например. Но это очень архитектурно зависимо, в отличие от С.

        • Alexey2005
          /#22920028

          C тоже архитектурно зависим. Ну то есть он конечно позволяет писать кроссплатформенный код, однако вовсе не делает автоматически любой написанный на нём код кроссплатформенным. И для поддержания архитектурной независимости приходится прилагать очень значительные усилия (в разы большие, чем для написания мультиплатформенного кода на том же Python). А когда их изначально не прилагали, а потом внезапно грянул гром…
          Достаточно вспомнить лишь, через какую боль пришлось пройти множеству программистов, когда осуществлялся переход с x86-32 на x86-64. Всего-то разрядность некоторых типов данных поменялась, а в итоге с адаптацией сишных программ была такая жесть, что до сих пор вспоминать страшно.

          • unsignedchar
            /#22920086

            "Всего-то разрядность некоторых типов данных поменялась"


            Ну, если в 100500 местах считалось, что sizeof(int) == sizeof(pointer), и pointer-pointer помещается в int, а потом вдруг оказывается, что это совсем не так… Конечно, все эти 100500 мест надо найти и исправить. Но теперь мы знаем, что так не надо делать, и не будем больше наступать на эти грабли? ;)

        • 0xd34df00d
          /#22920118

          Это если так совпало, что алгоритм мелкий, и настолько критичный по времени выполнения, что ради этого имеет смысл реализации на ассемблере. Какие нибудь матричные операции на SIMD регистрах, например.

          Ну, да. Или обращение цветов на картинке. Или даже подсчёт количества символов в строке.


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


          Но это очень архитектурно зависимо, в отличие от С.

          На C невозможно писать независимо от архитектуры и, более того, конкретного компилятора и его интерпретации конкретных UB, которые в вашем коде будут всегда.


          Более того, в C нет стандартного способа скомпилировать одну и ту же функцию, например, под SSE2, SSE4.2 и AVX2, и диспатчить вызовы в рантайме в зависимости от железа, надо обмазываться архитектурно-зависимыми проверками для этого.

      • saboteur_kiev
        /#22921884

        Если алгоритм достаточно мелкий, чтобы реализовать его на ассемблере в разумные сроки, то да, это делается легко, иногда с профитом в 3-10 раз.

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

        Если у вас есть примеры такого профита, было бы интересно послушать. Но я уверен на 99%, что все такие примеры будут приведены от людей с бородой свисающей ниже свитера.

        • 0xd34df00d
          /#22923172

          Задача, которую я часто привожу в комментах на эту тему: подсчитать количество вхождений символа в строку. Задача практически школьная, смешная, можно ожидать, что компиляторы такие вещи оптимизируют влёт. Однако, разница в 2-3 раза по сравнению с реализацией с интринсиками (что, конечно, технически не ассемблер, но и совсем не портабельный C).


          Другая задача, которой я занимался не далее как вчера вечером: есть большой массив размера N * 64 флоатов, нужно каждые следующие 64 флоата рассматривать как матрицу 8?8 и, по факту, умножить её на другую матрицу. Основной рабочий цикл на плюсах (для N = 1024 * 1024):


          for (size_t block = 0; block < 1024 * 1024; ++block)
          {
              const auto inBlock = &arr[block * 64];
              const auto outBlock = &output[block * 64];
          
              for (size_t y = 0; y < 8; ++y)
              {
                  for (size_t x = 0; x < 8; ++x)
                  {
                      float elem = 0;
                      for (size_t a = 0; a < 8; ++a)
                          elem += inBlock[8 * y + a] * idctMat[8 * x + a];
                      outBlock[8 * y + x] = elem;
                  }
              }
          }

          gcc 10 на мой машине с -O3 -march=native -ffast-math -ftree-vectorize -funroll-loops генерирует код, который работает 280 мс, clang 11 (без последних двух опций) — 200 мс.
          Написанный за три часа код с интринсиками под AVX:


          for (size_t block = 0; block < 1024 * 1024; ++block)
          {
              const auto inBlock = &arr[block * 64];
              const auto outBlock = &output[block * 64];
          
              for (size_t y = 0; y < 8; ++y)
              {
                  const auto inputRow = _mm256_loadu_ps(inBlock + y * 8);
                  for (size_t x = 0; x < 8; x += 4)
                  {
                      const auto as = _mm256_mul_ps(inputRow, idctRows[x]);
                      const auto bs = _mm256_mul_ps(inputRow, idctRows[x + 1]);
                      const auto cs = _mm256_mul_ps(inputRow, idctRows[x + 2]);
                      const auto ds = _mm256_mul_ps(inputRow, idctRows[x + 3]);
                      const auto a_plus_b = _mm256_hadd_ps(as, bs);
                      const auto c_plus_d = _mm256_hadd_ps(cs, ds);
                      const auto half_sums = _mm256_hadd_ps(a_plus_b, c_plus_d);
                      const auto upper_half = _mm256_castps256_ps128(_mm256_permute2f128_ps(half_sums, half_sums, 1));
                      const auto lower_half = _mm256_castps256_ps128(half_sums);
                      const auto full_sum = _mm_add_ps(lower_half, upper_half);
                      _mm_store_ps(&outBlock[y * 8 + x], full_sum);
                  }
              }
          }

          40 мс на моей машине. Разница в пять-семь раз, при этом здесь нет никаких сложных для машины вещей вроде разбиения большой матрицы на влезающие в L1-кеш блоки, ничего такого. Предельно тупая, предельно дружественная к оптимизатору (да и железу, вся IDCT-матрица влезает в 8 YMM-регистров, и ещё 8 остаётся на сами вычисления) задача.


          В качестве бонусных лулзов: внутренний цикл


          for (size_t block = 0; block < 1024 * 1024; ++block)
          {
              const auto inBlock = &arr[block * 64];
              const auto outBlock = &output[block * 64];
          
              for (size_t y = 0; y < 8; ++y)
                  for (size_t x = 0; x < 8; ++x)
                      outBlock[8 * y + x] = 0;
          }

          отрабатывает чуть больше 20 мс (что очень похоже на пропускную способность моей DDR3-1600-памяти). Если присваивать не ноль, а единицу, то время растёт до 35 мс. Если заменить всё выражение на outBlock[8 * y + x] = 0; на ++outBlock[8 * y + x];, то время снова падает до 20 мс (хотя ответ получается тот же, что и с единицей, так как массив инициализируется нулями). Это всё под клангом, под gcc все три варианта работают с примерно равной скоростью в 35-40 мс.

          • LittleAlien
            /#22928872

            Я попытался ради интереса повторить ваш тест, но не уверен, что правильно оформил интринсиковый вариант:
            gcc.godbolt.org/z/cbqGhYcas
            так правильно или нет? В частности, тип idctRows.
            Обычная версия кстати вполне успешно векторизуется:
            gcc.godbolt.org/z/e4K3Ps5z3
            только компилятор не умеет использовать горизонтальное сложение, и из-за этого долго тасует данные в регистрах, чтобы затем выдать красивую «простыню» из mulps/addps. Но сомневаюсь, что это даёт замедление аж в 5-7 раз, скорее вы сравнивали с невекторизованной версией.

            • 0xd34df00d
              /#22932446

              так правильно или нет? В частности, тип idctRows.

              Похоже. Вот


              полный код
              #include <iostream>
              #include <vector>
              #include <numeric>
              #include <chrono>
              #include <cmath>
              #include <array>
              #include <immintrin.h>
              
              using namespace std::chrono;
              
              template<typename T>
              decltype(auto) asMat(T&& arr, int x, int y)
              {
                  return arr[8 * y + x];
              }
              
              int main()
              {
                  const auto& arr = []
                  {
                      std::vector<float> arr;
                      arr.resize(64 * 1024 * 1024);
                      std::iota(arr.begin(), arr.end(), 10);
                      return arr;
                  } ();
              
                  std::vector<float> idctMat;
                  idctMat.resize(64);
                  for (size_t y = 0; y < 8; ++y)
                      for (size_t x = 0; x < 8; ++x)
                      {
                          if (x == 0)
                              asMat(idctMat, y, x) = std::sqrt(1. / 8.);
                          else
                              asMat(idctMat, y, x) = std::sqrt(1 / 4.) * std::cos(M_PI * (2. * x + 1) * y / 16.);
                      }
              
                  const std::array<__m256, 8> idctRows
                  {
                      _mm256_loadu_ps(&idctMat[0]),
                      _mm256_loadu_ps(&idctMat[8]),
                      _mm256_loadu_ps(&idctMat[16]),
                      _mm256_loadu_ps(&idctMat[24]),
                      _mm256_loadu_ps(&idctMat[32]),
                      _mm256_loadu_ps(&idctMat[40]),
                      _mm256_loadu_ps(&idctMat[48]),
                      _mm256_loadu_ps(&idctMat[56]),
                  };
              
                  std::vector<float> output;
                  output.resize(64 * 1024 * 1024);
              
                  auto start = high_resolution_clock::now();
              
                  for (size_t block = 0; block < 1024 * 1024; ++block)
                  {
                      const auto inBlock = &arr[block * 64];
                      const auto outBlock = &output[block * 64];
              
                      for (size_t y = 0; y < 8; ++y)
                      {
              #if 0
                          for (size_t x = 0; x < 8; ++x)
                          {
                              float elem = 0;
                              for (size_t a = 0; a < 8; ++a)
                                  elem += inBlock[8 * y + a] * idctMat[8 * x + a];
                              outBlock[8 * y + x] = elem;
                          }
              #else
                          const auto inputRow = _mm256_loadu_ps(inBlock + y * 8);
                          for (size_t x = 0; x < 8; x += 4)
                          {
                              const auto as = _mm256_mul_ps(inputRow, idctRows[x]);
                              const auto bs = _mm256_mul_ps(inputRow, idctRows[x + 1]);
                              const auto cs = _mm256_mul_ps(inputRow, idctRows[x + 2]);
                              const auto ds = _mm256_mul_ps(inputRow, idctRows[x + 3]);
                              const auto a_plus_b = _mm256_hadd_ps(as, bs);
                              const auto c_plus_d = _mm256_hadd_ps(cs, ds);
                              const auto half_sums = _mm256_hadd_ps(a_plus_b, c_plus_d);
                              const auto upper_half = _mm256_castps256_ps128(_mm256_permute2f128_ps(half_sums, half_sums, 1));
                              const auto lower_half = _mm256_castps256_ps128(half_sums);
                              const auto full_sum = _mm_add_ps(lower_half, upper_half);
                              _mm_store_ps(&outBlock[y * 8 + x], full_sum);
                          }
              #endif
                      }
                  }
              
                  auto end = high_resolution_clock::now();
              
                  const auto result = std::accumulate(output.begin(), output.end(), 0.0);
              
                  std::cout << result << std::endl;
                  std::cout << duration_cast<milliseconds>(end - start).count() << " ms" << std::endl;
              }

              • LittleAlien
                /#22934648

                Сравнил в том виде, в каком выкладывал в godbolt (Clang / gcc / интринсики, мс).
                i3-3250 (IvyBridge), AVX: 69 / 45 / 44
                i9-9900K (CoffeeLake), AVX: 52 / 38 / 40
                i9-9900K (CoffeeLake), FMA: 44 / 38 / 40
                Clang отстал на 10-60%, а gcc даже обогнал интринсики. В горизонтальные сложения он тоже не умеет, но зато сообразил перетасовать данные (матрицу 8*8?) ДО цикла.
                gcc.godbolt.org/z/PWjar9qar
                Что логично, я тоже об этом думал, глядя на попытки Clang-а, и в статьях по умножению матриц на Хабре что-то такое было. Похоже, gcc уже настолько крут, что читает Хабр :)
                Если серьёзно, есть с gcc одна странность — если вызывать функцию idctAV в вашем примере 1 раз, то работает быстро, а если несколько раз в цикле для большей стабильности результата, то скорость сильно проседает (только с gcc, с clang такого нет).

                Код
                    const int cycles = 10;
                    auto start = high_resolution_clock::now();
                
                    for (size_t c = 0; c < cycles; ++c) 
                      idctAV((float*)&arr[0], (float*)&output[0], (float*)&idctMat[0]);
                	 
                    auto end = high_resolution_clock::now();
                    const auto result = std::accumulate(output.begin(), output.end(), 0.0);
                    std::cout << result << std::endl;
                    std::cout << duration_cast<milliseconds>((end - start) / cycles).count() << " ms" << std::endl;
                

          • KGeist
            /#22928974

            Кроме векторизации и аллокации регистров, где ещё человек может обойти компилятор?

    • hottabxp
      /#22923028

      Я вот не слышал, что питон медленный, что я делаю не так?

      Большинство тех, кто говорит что Python медленный из этих — «Слышал звон, да не знаю где он». Они просто где-то услышали что C++ быстрый, а Python медленный — и трезвонят на весь интернет об этом. При всем этом, они не знают, в каких случаях C++ может выиграть в производительности.

      • 0xd34df00d
        /#22923120

        Вы остановились на самом интересном месте. В каких же случаях может, а в каких — нет?

  19. 0xd34df00d
    /#22918900

    del

  20. hellamps
    /#22920262

    кстати, самый главный хейт забыли же — совместимость 2 и 3 версии :)