Примечание: всё, что говорится в этой статье, можно отнести и к любому другому языку с динамической типизацией (скажем, Ruby). Я делюсь своим опытом и мытарствами конкретно по Python только потому, что сам пользуюсь этим языком.

Изначально я осваивал программирование на Python, и этот язык до сих пор занимает особое место в моем сердце. Именно он научил меня, как нужно подходить к программированию, как моделировать проблему, которую должен решить код, и общаться с компьютером. Свою текущую работу (как и предшествующую) я получил благодаря знанию Python. Я был его фанатом и евангелистом, проводил много времени в сообществах по Python – помогал другим и учился сам. Когда я стал выступать в роли наставника для новичков, то из языков выбрал для себя Python.
Хорошо помню, как у нас с другом состоялся такой разговор.

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

Как же я ошибался. На тот момент я доводил код до продакшна на Python, но пользователей у нас было немного. Опыта работы с другими языками у меня не имелось вообще.

Когда я пришел в стартап, где работаю сейчас (команда состояла из трех разработчиков), то начал изучать Go. Я отвечал за разработку нескольких сервисов. Для части из них я выбрал Python, потому что был с ним лучше всего знаком и мог быстро выдавать результат. Для отдельных задач больше подходил Django. С ними мне удавалось быстро доводить код до релиза, и так я создал ряд сервисов, которые до сих пор в ходу.

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

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

Разработка на кофейной гуще


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

Для нескольких модулей мне не удалось найти подсказок в среде разработки. У меня вошло в привычку держать исходный код открытым в соседней вкладке и постоянно сверяться по мере того, как пишу новый код. Без типов (и подсказок типов) было сложно понять, как использовать функцию. С точки зрения рефакторинга наличие в языке типов многое меняет: один взгляд на аннотацию типа – и я уже знаю, что делать.

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

Годы разработки на Go выработали во мне приятное убеждение: компилируется – значит работает (а с Rust еще того хлеще: компилируется – значит не возникнет проблем с памятью и состояния гонки). Компиляция оперативно дает обратную связь, что сильно облегчает внесение изменений.

Юнит-тесты и подсказки типов


Подсказки типов до некоторой степени решают проблему, но баги всё равно проскакивают. Оптимальный путь – юнит-тестирование. Однако это не всегда возможно, например, бывает, что в команде сложно установить единые правила или же вам передают legacy-проект. Но работает ли на практике введение строгих правил? Можно выстроить такой пайплайн CI/CD, что покрытие достигнет 100%, но от этого будет страдать производительность команды и, вне всякого сомнения, нервы сотрудников.

Мне нужна система, которая сводит риск допустить ошибку к минимуму, и при помощи юнит-тестирования и пайплайнов CI/CD я могу ее получить. Но типобезопасность обеспечивает то же самое просто по умолчанию.

Я сделал для себя вывод: реальная жизнь далека от идеала. Кодовые базы без комментариев, документов, юнит-тестов – обычное дело. С этим неизбежно сталкиваешься, если работаешь в команде с крупной кодовой базой. И при таком раскладе работа на Python превращается в чистое мучение.

Дает ли Python преимущество в скорости?


Когда я имел дело с языками со строгой типизацией, то думал: ну вот, придется теперь определять везде типы, заранее создавать структуры (или классы) и иногда сочинять всё это из головы, потому что информации еще нет или окончательное решение не принято. И все эти жалобы вполне справедливы. Прототипирование на Rust займет у меня много времени. Python же даст возможность быстро дойти до релиза… но только один раз.

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

Проводить рефакторинг на Python мне тяжело. Не могу даже пожаловаться на того, кто его написал – это ведь был я сам, всего несколько лет назад! Можно было сгладить эти проблемы при помощи юнит-тестов и подсказок типов. Но удалось бы мне сохранить высокую скорость работы, если бы я тратил на всё это столько времени?

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

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


  1. Ilia1099
    00.00.0000 00:00
    +6

    Не претендую на звание гуру
    Просто поделюсь мнением
    Что касается аннотации типов в питоне - она все равно дает преимущество в скорости написания кода, по сравнению со строгой типизацией
    Я сейчас вынужден писать на Джаве, и синтаксически питон для меня выглядит более удобным, как пример, отсутствие дефолтных параметров функции в Джаве приводит к разрастанию кода - method overloading, который еще и усложняет логику, как по мне

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

    Для себя я осознал, что динамическая типизация не позволяет писать код как попало, она позволяет уменьшить количество действий для результата, но логика кода должна быть максимально прозрачной


    1. freemanY
      00.00.0000 00:00

      Если бы динамическая типизация была плохой, то наверное вымерла бы.


      1. DMGarikk
        00.00.0000 00:00
        +1

        Если бы динамическая типизация была плохой, то наверное вымерла бы.

        Совершенно не показатель (оглянулся на js)
        главный показатель — скорость реализации, а если она высокая, бизнес что угодно простит


  1. Lukerman
    00.00.0000 00:00

    Повёлся на заголовок )...

    Шурик и трещетку сравнивают


  1. Hheimerd
    00.00.0000 00:00

    Не волнуйся, скоро популяризируют какой-нибудь TypeThon и будет у питонистов счастье


    1. GBR-613
      00.00.0000 00:00
      +1

      Уже есть и, кажется, даже не один.

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


  1. ef_end_y
    00.00.0000 00:00
    +1

    С одной стороны автор критикует одну из характеристик Пайтона, с другой стороны рекламирует свои проекты на нем.

    Язык - это совокупность множества характеристик и идеологий. Автор смотрит на легковую машину и говорит "а у трактора-то проходимость лучше, там гусеницы. Колеса отстой, гусеницы рулят". Типизация это далеко не единственная фича, которая влияет на качество кода. Если код написан так, что из контекста, аннотации типов и docstring непонятно что принимает и возвращает метод - этот метод тебе не нужен, зачеркнуто, ты гомнокодер


  1. barloc
    00.00.0000 00:00
    -1

    Годы разработки на Go выработали во мне приятное убеждение: компилируется – значит работает 

    Да ладно, правда? В питоне тоже можно сказать - запускается - это значит работает.

    Кажется, что стоит научиться писать просто что на питоне, что на го - тогда попроще будет.


  1. danilovmy
    00.00.0000 00:00
    +1

    Мне кажется, или у автора просто небольшой опыт и слабое абстрактное мышление? Я работаю в основном с: Assembler, C, Fortran, Pascal, VB, JS, Python. Я знаю преимущества этих языков и использую их. Я никогда не сравнивал, какой из них лучше. Хотя мое мнение, ассемблер лучше всех :) но это не мешает мне работать с большими проектами на других языках, например, Delphi.


    1. ef_end_y
      00.00.0000 00:00

      А что на асме прогаете? Я думал, что единственная для него ниша на данный момент - это компиляторы


      1. danilovmy
        00.00.0000 00:00

        Привет. Программировал всякие embedded solutions типа Webasto на RISC-CISC процессорах, там, где по правовым нормам нельзя использовать существующие решения от "потенциального противника". Подобных заказов сейчас появилось много.


      1. Ktator
        00.00.0000 00:00

        Ассемблер очень важен, когда требуется предельная производительность. Даже если не писать на нём, то знать, во что компилируется ваш код.

        Кроме того, современные компиляторы пока не очень хорошо умеют в векторизацию, а AVX2 – это не только SIMD, позволяющий делать несколько операций за раз, но и полкилобайта регистровой памяти, самой быстрой из возможных (а AVX512 и того больше).


  1. mshadow
    00.00.0000 00:00

    Я честно говоря только "вкатываюсь в IT", поэтому пусть более опытные ребята поправят, но:

    1. выражение "Годы разработки на Go выработали во мне приятное убеждение: компилируется – значит работает" - на мой взгляд далеко от реальности, статическая типизация только гарантирует, что вы типы не перепутали, а совсем не проверяет логику и никак не "устраняет необходимость тестирования"

    2. Немного пробовал писать на java и строгая типизация - это кошмар в плане количества кода, которое будет делать то же действие, что и python. В некоторых случаях, на мой взгляд, грамотное название функций и doc-string - выглядят более качественным решением.

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


    1. DMGarikk
      00.00.0000 00:00
      +3

      В некоторых случаях, на мой взгляд, грамотное название функций и doc-string — выглядят более качественным решением.

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

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


    1. fshp
      00.00.0000 00:00

      Почему-то в качестве примера статической типизации выбирают самый неудачный. В java проблема не в типизации, а в языке. Хотя в последних редакциях уже лучше.

      Возьмите scala или haskell. Там и типизация статическая, и, внезапно, типы в большинстве случаев можно не указывать как в Python.


  1. teiuaz
    00.00.0000 00:00

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

    А вот то, что в питоне у переменных нет типов, типы есть только у объектов, на которые переменная ссылается и которые создаются во время выполнения (динамическая типизация), это несёт как плюсы, так и минусы.

    Для меня динамическая типизация + концепция всеобъектности + простой и понятный синтаксис языка дают два важных преимущества над другими языками: объем кода и его простота. Это конечно все с пометкой, что вы знаете как писать мало и просто на Питоне. А вот это уже совсем не просто. Это я понял, когда посмотрел как люди пишут код на собеседованиях и как плюсовики пишут код на питоне ;)