Главная страница
Навигация по странице:

  • Проверь актуальность

  • Раннее выявление ошибок

  • Читаемость, понятность и поддерживаемость кода

  • Важно Подсказки типов значительно улучшают читаемость кода и облегчают его сопровождение и поддержку.Помощь IDE при разработке

  • Типизированный_Python_для_профессиональной_разработки. Профессиональной разработки


    Скачать 3.38 Mb.
    НазваниеПрофессиональной разработки
    АнкорPython
    Дата15.12.2022
    Размер3.38 Mb.
    Формат файлаpdf
    Имя файлаТипизированный_Python_для_профессиональной_разработки.pdf
    ТипДокументы
    #847528
    страница1 из 5
      1   2   3   4   5
    Типизированный Python для
    профессиональной разработки
    Алексей Голобурдин, команда Диджитализируй!
    обложка —
    Васильев Никита
    ,
    nikita.vasiliev@math.msu.ru
    Проверь актуальность
    Актуальную на сегодняшний день версию книги всегда можно бесплатно скачать здесь t.me/t0digital/151
    . Данная редакция — от 27 мая 2022 1600,
    changelog версий на последних страницах.
    Цель этой книги — помочь тебе научиться писать более красивые, надёжные и легко сопровождаемые программы на Python. То, о чём мы здесь будем говорить, это не начальный уровень владения языком, предполагается, что ты уже минимально умеешь программировать, но хочешь научиться делать это лучше.
    И это — отличная цель, к которой мы вместе будем двигаться на протяжении ближайших часов!
    Этот материал есть также в видео формате на моём YouTube —
    «Диджитализируй!»
    Также обращаю внимание, что на момент написания этих строк готовится перезапуск моего авторского курса Основы компьютерных и веб-технологий на course01.to.digital
    , запуск планируется в июне 2022, если ты читаешь этот материал позже, то вполне вероятно, что на курс уже снова можно записаться.
    Итак!
    Часто в учебниках и курсах по Python не уделяют должного внимания типизации и некоторым структурам, в то время как они очень важны и могут значительно, просто драматически улучшить твой код.
    В ревью кода начинающих разработчиков часто видны результаты того, что в учебных материалах не уделяется отдельное внимание вопросам типизации. В коде не используются подсказки типов, используются неправильно, не лучшим образом выбираются типы для разных данных в приложении итак далее. Качество
    программы и её надёжность страдают — а это гораздо более важные параметры,
    чем многие поначалу думают. Поначалу кажется, что я написал программу, она в моих идеальных условиях работает и этого достаточно. Нонет, этого недостаточно.
    Наличие функциональности это одно, а надёжность этой функциональности и качество реализации этой функциональности это совсем другое. Наличие функциональности это когда вы видите обувь и думаете — о, отлично, можно её
    надеть и пойти в ней куда-то. А надёжность и качество реализации этой функциональности это про то, что у вас не треснет подошва где-то на улице, в обувь не будет попадать вода, обувь не будет натирать вам ноги, она не потеряет быстро приличный вида также это про то, что обувь легка в эксплуатации, её можно легко протереть, её можно ремонтировать и многое другое.
    То, что мы написали программу иона имеет функциональность — это вовсе не означает, что программа действительно хороша. В этой небольшой книге мы поговорим о том, как разрабатывать, думая не только о функциональности, но и о качестве и надёжности её реализации.
    Оглавление
    Оглавление есть в самом PDF документе. В программе просмотра PDF можно найти раздел Оглавление и открыть его.
    Мы поговорим о типизации в Python, поговорим о нескольких структурах и встроенных типах,
    Optional
    ,
    Iterable
    ,
    Sequence
    ,
    Mapping
    ,
    Callable
    ,
    TypeVar и др.
    Напишем приложение погоды, используя эти типы и поясняя походу некоторые архитектурные моменты выбора того или иного подхода. Смотри видео версию этой книги на YouTube и читай обязательно до конца
    Обещаю, что после проработки этого материала твой код больше никогда не будет прежним. Буквально — драматическое улучшение кода гарантировано. Как пишут в англоязычных книжках, dramatic Поднимаемые вопросы актуальны, кстати, не только для Python, говорить мы будем он м, но аналогичные подходы применимы и к PHP, TypeScript и тд. Подходы к написанию качественного ПО схожи для разных языков программирования,
    выступающих просто инструментом реализации задумок автора кода.
    Говорить мы здесь будем о версии Python 3.10. В предыдущих версиях Python некоторые аспекты работают чуть иначе (нужно импортировать некоторые типы из typing
    , например, но это не столь критично.
    Опчки! Время подписаться /
    Telegram
    / Начать нужно с разговора о самой типизации и о том, почему этому нужно уделять тщательное внимание. Итак, подсказки типов Python или, что тоже самое, type hinting.

    Type Что делает любая программа Оперирует данными, то есть какие-то данные принимает на вход, какие-то данные отдаёт на выхода внутри данные как-то трансформирует, обрабатывает и передаёт в разные функции, классы, модули итак далее. И весь вопрос в том, в каком виде и формате программа внутри себя эти данные передаёт! То есть — какие типы данных для этого используются. Часто одни и те же данные можно передавать внутри приложения строкой, списком, кортежем,
    словарём и массой других способов.
    Как все мы знаем, Python это язык с динамической типизацией. Что означает динамическая типизация Что тип переменной определяется не в момент создания переменной, а в момент присваивания значения этой переменной. Мы можем сохранить в переменную строку, потом число, потом список, и это будет работать.
    Фактически интерпретатор Python сам выводит типы данных и мы их нигде не указываем, вообще не думаем об этом — просто используем то, что нам нужно в текущий момент.
    Так зачем же вводить type hinting в язык с динамической типизацией А я напомню,
    что в Python сейчас есть type hinting, то есть подсказки типов, они же есть в PHP, а в даже разработали TypeScipt, отдельный язык программирования, который является надстройкой над JS и вводит типизацию. Зачем это всё делается, для чего?
    Вроде скриптовые языки, не надо писать типы, думать о них, и всё прекрасно, а тут рази вводят какие-то типы данных.
    Зачем в динамически типизированном языке вводить явное указание типов?
    Раннее выявление ошибок
    Первое — это ранее выявление ошибок. Есть у нас некая программа и есть в этой некой программе ошибка. Когда мы можем её выявить Мы можем выявить её на этапе написания программы, мы можем выявить её на этапе подготовки программы user
    =
    "Пётр"
    user
    =
    120560
    user
    =
    {
    "name"
    :
    "Пётр"
    ,
    "username"
    :
    "petr@email.com"
    ,
    "id"
    :
    120560
    }
    user
    =
    (
    "Пётр"
    ,
    "petr@email.com"
    ,
    120560
    )
    к разворачиванию на сервере, или мы можем выявить её на этапе runtime, то есть когда программа уже опубликована на сервере, ей пользуются пользователи.
    Как выдумаете, на каком этапе лучше выявлять ошибки Очевидно — чем раньше,
    тем лучше. Если ошибки долетают до пользователей, то это максимально плохо.
    Почему?
    Во-первых, потому что пользователи недовольны, а раз пользователи недовольны,
    то много денег мыс нашим программным продуктом не заработаем, так как люди не будут охотно его покупать и рекомендовать другим. К тому же очень неприятно, что мы занимаемся любимым делом, активно трудимся, реализуем сложные алгоритмы,
    а результатом нашего труда пользователи недовольны. И винить-то объективно некого, кроме нас самих. Непорядочек, непорядочек!
    Во-вторых, недовольные пользователи обращаются в техподдержку, создают тикеты, которые спускаются потом на разработку — это всё тратит деньги компании.
    Если ничего не ломается, то обращений в поддержку мало, тикетов мало, а разработчики заняты разработкой новых фичей продукта, а непостоянными правками отвалившейся старой логики. Постоянные поломки это постоянные финансовые потери.
    В-третьих, из-за ошибок, которые видят пользователи, компания несёт репутационные потери. Пользователи пишут негативные отзывы, они легко гуглятся другими потенциальными пользователями, СМИ, инвесторами, всё это в конечном итоге негативно влияет и на капитализацию компании, и на возможности привлечения инвестиций, и на чистую прибыль компании, если она вообще есть.
    Если мы хотим быть профессиональными высокооплачиваемыми специалистами, то наша задача — генерировать через результаты нашей работы радость и прибыль, а не поток проблем и убытков.
    Поэтому важнейшая задача для нас — сделать так, чтобы до пользователей не доходило ни одной ошибки. Для достижения этой цели нужен системный подход,
    одной внимательности в процессе программирования мало. Нужна выверенная система, алгоритм действий, инструментарий, который не позволит ошибкам дойти до пользователей.
    Какой это может быть инструментарий Это могут быть автотесты. Однако первый принцип тестирования гласит, что тестирование может показать наличие дефектов в

    программе, ноне доказать их отсутствие. Тесты это хорошо, ноне на одних только тестах всё держится. Чем больше капканов для разных видов ошибок расставлено,
    тем надёжнее программа и крепче сон разработчика. А что, в конце концов, может быть важнее крепкого, здорового сна разработчика
    Помимо автотестов (и ручного тестирования людьми) можно проверять корректность использования типов специальными инструментами. Например,
    компилятор Rust — прооосто красавчик Он на этапе компиляции выявляет огромное количество проблем и попросту не даёт скомпилировать программу, если видит в ней места с ошибками. Какая-то функция может вернуть успешный результат или условный null и вызывающая функция не обрабатывает сценарий с null
    ? Вот тебе потенциальная серьёзная ошибка. Компилятор об этом скажет ивам придется сделать всё красиво, обработать все такие моменты и они не дойдут до рантайма.
    Штука в том, что в случае с динамически типизированными языками вроде очень сложно написать инструментарий, который бы выполнял проверки по типам,
    потому что в каждый конкретный момент времени непонятно какой тип в переменной. И для того, чтобы этому инструментарию помогать, вводят подсказки типов вили типизацию в JavaScript в виде отдельного языка TypeScript,
    компилирующегося в JavaScript. Это то, что помогает выявлять ошибки на этапе до runtime. Либо на этапе подготовки сборки программы, либо даже на этапе написания программы непосредственно в редакторе кода.
    Инструмент видит, что вот здесь такой-то тип данных, и если он используется некорректно, то инструмент покажет ошибку и эта ошибка не уйдёт в рантайм и пользователи не словят эту ошибку, а мы как разработчик не схлопочем по макушке от руководства. Прекрасно Прекрасно!
    То есть, ещё раз, первое, для чего вводят подсказки типов — как можно более ранее выявление ошибок, в идеале в редакторе кода в вашей IDE, либо хотя бы на этапе проверки программы переде сборкой и публикацией на сервер.
    На википедии есть прекрасная страница про системы типов,
    Type system
    :
    Wikipedia
    The main purpose of a type system is to reduce possibilities for bugs in computer programs by defining interfaces between different parts of a computer program, and then checking that the parts have been connected in a consistent То есть главной целью системы типов является уменьшение вероятности ошибок в компьютерных программах путём определения интерфейсов между разными частями программы и последующей проверки, что эти части соединены друг с другом правильным образом через эти интерфейсы. Под интерфейсами тут подразумеваются собственно типы данных, например, какие-то конкретные классы,
    которые описывают конкретные поля и методы
    Допустим, у насесть вот такая функция:
    Под user тут подразумевается объект юзера, например,
    ORM объект, то есть запись из базы данных, преобразованная в специальный Python объект. Человек,
    написавший код, это знает. В момент написания кода знает. Через месяц он об этом совершенно точно забудет, а человек, не писавший этот код, об этом знать вовсе не может. По сигнатуре функции def validate_user(user)
    нельзя понять, какой тип данных ожидается в user
    , но при этом очень легко сделать предположение об этом типе — и ошибиться.
    Спустя полгода дописывается какой-то новый код и функция validate_user в нём внезапно начинает вызываться с аргументом user
    , который равен не ORM объекту, а числу — просто потому что совсем неочевидно, что в user на самом деле подразумевается не число:
    Этот код упадёт только в рантайме. Потому что IDE или статический анализатор кода не смогут понять, что тут есть какая-то ошибка.
    Как сделать так, чтобы мы узнали об ошибке до этапа рантайма? Явным образом указать тип для атрибута user
    , например, если это экземпляр датакласса
    User
    , то так
    (о датаклассах мы поговорим подробнее дальше Проверяет юзера, райзит исключение, если с ним что-то не так validate_user_on_server
    (
    user
    )
    check_username
    (
    user
    )
    check_birthday
    (
    user
    )
    user_id
    =
    123
    validate_user
    (
    user_id
    )
    from dataclasses import dataclass import datetime
    @dataclass class
    User
    :
    username
    :
    str created_at
    :
    datetime datetime birthday
    :
    datetime datetime
    |
    None def validate_user
    (
    user
    :
    User
    ):

    Датакласс определяет просто структуру данных с полями username
    , created_at и, причём тип поля username
    — строка, типа хранит
    None или значение типа И теперь такой код:
    подкрасится ошибкой уже в IDE на этапе написания кода (например, вили настроенном VS Code или nvim
    ), а также код упадёт с ошибкой в статическом анализаторе кода, который запустится при сборке проекта перед его публикацией на сервер.
    Получается, что наша программа стала надёжнее, так как пользователи не увидят многие ошибки, они будут выявлены и исправлены на ранних этапах. Да, при этом надо писать типы, вводить их, думать, но кому сейчас легко. Нам платят деньги за качественный софт, а качественный софт это в первую очередь надёжный софт, то "Проверяет юзера, райзит исключение, если с ним что-то не так validate_user_on_server
    (
    user
    )
    check_username
    (
    user
    )
    check_birthday
    (
    user
    )
    user_id
    =
    123
    validate_user
    (
    user_id
    )
    есть софт, который не падает в рантайме с непонятными пользователю ошибками вроде
    AttributeError
    Важно
    Цена исправления ошибки в программе тем меньше, чем раньше этап, на котором эта ошибка выявлена. Главная причина введения системы типов уменьшение вероятности возникновения ошибок в рантайме, то есть при эксплуатации приложения.
    Читаемость, понятность и поддерживаемость кода
    Вернёмся к нашей функции:
    Представим, что ты пока не очень опытный программист, который только пришел в компанию, и тебе дали задачу добавить ещё одну проверку по юзеру, чтобы валидацию проходили только пользователи, созданные вчера или раньше. Ты этот код очевидно не писал, видишь его впервые и как тут всё работает ещё не знаешь.
    Тут user
    — это что Это словарь key-value
    ? Это
    ORM
    объект Это
    Pydantic модель У
    этого юзера тут есть поле created_at
    , дата создания, или нет Оно нам в нашей задаче ведь нужно будет.
    Как ответить на эти вопросы Перелопачивать код, который вызывает эту нашу функцию validate_user
    . А там тоже непонятно, что в user лежит. Там 100500 функций выше, и где и когда там появляется user и что в нём лежит — большой-большой вопрос плюс мы нашли 2 сценария, водном наша функция вызывается сом, то есть user это словарь, а в другом сценарии функция вызывается с ORM моделью, и возможно еще какой-то код вызывает еще как-то иначе нашу горе-функцию validate_user
    . Вот как с этим жить Вам может понадобиться конкретно перелопатить весь проект, чтобы понять, как добавить абсолютно простейшую проверку.
    А если бы здесь был такой код — то все вопросы решились бы мгновенно Проверяет юзера, райзит исключение, если с ним что-то не так validate_user_on_server
    (
    user
    )
    check_username
    (
    user
    )
    check_birthday
    (
    user
    )
    Тут понятно, чем является структура user
    . Тут очевидно, что это класс и у него есть такие-то атрибуты, дата создания юзера, юзернейм и дата рождения юзера. Причем даты хранятся тут не как строки, а как datetime, то есть все вопросы у нас мгновенно снимаются.
    Чтение кода значительно облегчилось. Нам понятно, что заданные в user
    , у нас больше нет вопросов, каких обработать. Если вы хотите, чтобы вашим кодом было приятно пользоваться — подсказки типов это обязательный инструмент для вас. Что код принимает на вход Что он отдаёт на выход На эти вопросы отвечают подсказки типов.
    Важно
    Подсказки типов значительно улучшают читаемость кода и облегчают его сопровождение и поддержку.
    Помощь IDE при разработке
    Если мы пользуемся подсказками типов, то наша IDE уже на этапе написания кода будет подсказывать нам места с потенциальными ошибками, о чем говорилось выше, то есть мы на самом самом раннем этапе некоторые типы ошибок предотвратим, что хорошо, но еще и разрабатывать нам будет удобнее, потому что будет работать автодополнение и все плюшки нашей IDE. В реальности использовать код, который написан с типами, гораздо проще, приятнее, удобнее и быстрее, чем код без явно указанных типов, так как, если типы известны нашей IDE, то она from dataclasses import dataclass import datetime
    @dataclass class
    User
    :
    username
    :
    int created_at
    :
    datetime datetime birthday
    :
    datetime datetime
    |
    None def validate_user
    (
    user
    :
    Проверяет юзера, райзит исключение, если с ним что-то не так validate_user_on_server
    (
    user
    )
    check_username
    (
    user
    )
    check_birthday
    (
    user
    )
    всячески будет помогать нам, делать автодополнения, предлагать сразу правильные методы, выполнять проверки итак далее.
    Важно
    При использовании подсказок типов IDE в значительной степени помогает писать код, в полной мере работают подсказки, автодополнения и тд. Чтобы действительно помогала нам писать код, пользоваться подсказками типов просто необходимо of Явное лучше неявного. Когда у нас явно и конкретно указан тип данных — это хорошо и это соответствует дзену. Когда женам приходится неявно домысливать,
    что тут, наверное, (наверное) строка или int
    — это плохо и это не соответствует дзену.
    Простое лучше сложного Подсказки типов это просто, во всяком случае точно проще, чем попытки описать тип данных в докстринге функции в томили ином формате.
    Удобочитаемость важна. Опять же, обсудили выше в примере с юзером, в котором непонятно какая структура без явной типизации.
    Должен быть один — и желательно всего один — способ это сделать Это как раз механизм type hinting. Не надо описывать типы в докстрингах или в формате какого- то внешнего инструмента с птичьим языком. Есть родной механизм в языке программирования, который решает задачу описания типов данных.
      1   2   3   4   5


    написать администратору сайта