Изначально я осваивал программирование на 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)
ef_end_y
00.00.0000 00:00+1С одной стороны автор критикует одну из характеристик Пайтона, с другой стороны рекламирует свои проекты на нем.
Язык - это совокупность множества характеристик и идеологий. Автор смотрит на легковую машину и говорит "а у трактора-то проходимость лучше, там гусеницы. Колеса отстой, гусеницы рулят". Типизация это далеко не единственная фича, которая влияет на качество кода. Если код написан так, что из контекста, аннотации типов и docstring непонятно что принимает и возвращает метод - этот метод тебе не нужен, зачеркнуто, ты гомнокодер
barloc
00.00.0000 00:00-1Годы разработки на Go выработали во мне приятное убеждение: компилируется – значит работает
Да ладно, правда? В питоне тоже можно сказать - запускается - это значит работает.
Кажется, что стоит научиться писать просто что на питоне, что на го - тогда попроще будет.
danilovmy
00.00.0000 00:00+1Мне кажется, или у автора просто небольшой опыт и слабое абстрактное мышление? Я работаю в основном с: Assembler, C, Fortran, Pascal, VB, JS, Python. Я знаю преимущества этих языков и использую их. Я никогда не сравнивал, какой из них лучше. Хотя мое мнение, ассемблер лучше всех :) но это не мешает мне работать с большими проектами на других языках, например, Delphi.
ef_end_y
00.00.0000 00:00А что на асме прогаете? Я думал, что единственная для него ниша на данный момент - это компиляторы
danilovmy
00.00.0000 00:00Привет. Программировал всякие embedded solutions типа Webasto на RISC-CISC процессорах, там, где по правовым нормам нельзя использовать существующие решения от "потенциального противника". Подобных заказов сейчас появилось много.
Ktator
00.00.0000 00:00Ассемблер очень важен, когда требуется предельная производительность. Даже если не писать на нём, то знать, во что компилируется ваш код.
Кроме того, современные компиляторы пока не очень хорошо умеют в векторизацию, а AVX2 – это не только SIMD, позволяющий делать несколько операций за раз, но и полкилобайта регистровой памяти, самой быстрой из возможных (а AVX512 и того больше).
mshadow
00.00.0000 00:00Я честно говоря только "вкатываюсь в IT", поэтому пусть более опытные ребята поправят, но:
выражение "Годы разработки на Go выработали во мне приятное убеждение: компилируется – значит работает" - на мой взгляд далеко от реальности, статическая типизация только гарантирует, что вы типы не перепутали, а совсем не проверяет логику и никак не "устраняет необходимость тестирования"
-
Немного пробовал писать на java и строгая типизация - это кошмар в плане количества кода, которое будет делать то же действие, что и python. В некоторых случаях, на мой взгляд, грамотное название функций и doc-string - выглядят более качественным решением.
Мне до сих пор интересно, никто не проводил исследования какое количество ошибок в разработке "решает" строгая типизация, потому что, я бы не удивился, если "цена" за устранение ошибок - окажется слишком высокой. В данном случаи рассматривается "гипотетический вариант", что типизация нужна - чтобы убрать ошибки.
DMGarikk
00.00.0000 00:00+3В некоторых случаях, на мой взгляд, грамотное название функций и doc-string — выглядят более качественным решением.
практически во всех проектах где я работал, после перехода через определенную грань сложности, в питон начинают тащить типизацию, валидацию, линтеры и абстракции… прямо как в яве
ловить ошибки типов в проекте на миллионы строк кода на питоне, это охренеть какое занятие… учитывая что они только в рантайме вылезают
fshp
00.00.0000 00:00Почему-то в качестве примера статической типизации выбирают самый неудачный. В java проблема не в типизации, а в языке. Хотя в последних редакциях уже лучше.
Возьмите scala или haskell. Там и типизация статическая, и, внезапно, типы в большинстве случаев можно не указывать как в Python.
teiuaz
00.00.0000 00:00Со строгой типизацией в питоне как раз таки все классно, то есть очень мало неявных преобразований связанных с операциями над объектами разных типов, вроде сложения строк и чисел. Питон просто падает на таких операциях. Это сокращает количество трудноуловимых багов.
А вот то, что в питоне у переменных нет типов, типы есть только у объектов, на которые переменная ссылается и которые создаются во время выполнения (динамическая типизация), это несёт как плюсы, так и минусы.
Для меня динамическая типизация + концепция всеобъектности + простой и понятный синтаксис языка дают два важных преимущества над другими языками: объем кода и его простота. Это конечно все с пометкой, что вы знаете как писать мало и просто на Питоне. А вот это уже совсем не просто. Это я понял, когда посмотрел как люди пишут код на собеседованиях и как плюсовики пишут код на питоне ;)
Ilia1099
Не претендую на звание гуру
Просто поделюсь мнением
Что касается аннотации типов в питоне - она все равно дает преимущество в скорости написания кода, по сравнению со строгой типизацией
Я сейчас вынужден писать на Джаве, и синтаксически питон для меня выглядит более удобным, как пример, отсутствие дефолтных параметров функции в Джаве приводит к разрастанию кода - method overloading, который еще и усложняет логику, как по мне
А по поводу строгой типизации как своего рода документации - тут и да и нет как по мне.
Лично я все равно хотел бы чтобы были нормальные достринги, мельтешня указателей типов (опять таки, для меня) замыливает глаз.
И сейчас столкнувшись с легаси на проекте, я местами матерился потому что строгая типизация спагетти кода ну никак не давала понять что, как или зачем и почему не иначе (может мне не хватает опыта конечно)
Для себя я осознал, что динамическая типизация не позволяет писать код как попало, она позволяет уменьшить количество действий для результата, но логика кода должна быть максимально прозрачной
freemanY
Если бы динамическая типизация была плохой, то наверное вымерла бы.
DMGarikk
Совершенно не показатель (оглянулся на js)
главный показатель — скорость реализации, а если она высокая, бизнес что угодно простит