Типизированный_Python_для_профессиональной_разработки. Профессиональной разработки
Скачать 3.38 Mb.
|
Важно! По возможности вместо указания в типах list , dict и тп указывай классы, Sequence , Mapping Во-первых, это позволит менять конкретные реализации, удовлетворяющие условию итерабельности, доступа по индексу или доступа по ключу соответственно, решение получится более гибким. Во-вторых, анализатор кода mypy будет лучше работать с такими типами данных , что позволит избежать некоторых осложнений. Если от контейнера требуется итерабельность (чтобы поданным в контейнере можно было итерироваться, то есть проходить в цикле, то стоит указать, который гарантирует именно итерабельность, вместо того, чтобы указывать одни из возможных реализаций итерабельных контейнеров вроде list или tuple , несущих помимо собственно итерабельности и другие свойства. Если от контейнера требуется доступ по индексу, то стоит указать Sequence , а не одну из возможных реализаций вроде Наконец, если требуется доступ по ключу, то следует указать Пару слов стоит сказать про кортежи, если размер кортежа важен и мы хотим его прямо указать в типе, то это можно сделать так: Если количество элементов неизвестно — можно так typing import Mapping def smth ( some_users : Mapping [ str , User ]) -> None : ( some_users [ "alex" ]) smth ({ "alex" : User ( birthday = datetime fromisoformat ( "1990-01-01" )), "petr" : User ( birthday = datetime fromisoformat ( "1988-10-23" )) }) three_ints = tuple [ int , int , int ] Дженерики Что если мы хотим написать обобщённую функцию, которая принимает на вход итерируемую структуру, то есть структуру, по которой можно итерироваться, и возвращает результат первой итерации? Как видите, типом данных в этой итерируемой структуре iterable могут быть любые данные, анаши в функции first говорят буквально, что функция принимает на вход итерабельную структуру данных, каждый элемент которой имеет тип T , и функция возвращает тот же тип T . Тип T при этом может быть любым. Это так называемые дженерики, то есть обобщённые типы. Причём имя T здесь это пример, он часто используется именно так, T , от Type, но название типа может быть и любым другим. Помимо дженериков можно сохранять отдельные типы для лучшей читаемости кода и подсказки читателю, что именно заданные здесь хранятся = tuple [ int , ...] from typing import TypeVar , Iterable T = TypeVar ( "T" ) def first ( iterable : Iterable [ T ]) -> T | None : for element in iterable : return element print ( first ([ "one" , "two" ])) # one print ( first (( 100 , 200 ))) # 200 from dataclasses import dataclass Phone = str @dataclass class User : user_id : int phone : Phone Мы уже использовали это в коде приложения погоды, когда задавали псевдоним для градусов Цельсия. Вызываемые объекты Как известно функции в Python это обычные объекты, которые можно передавать в другие функции, возвращать из других функций и тп, поэтому для них тоже есть свой тип Здесь для аргумента operation функции process_operation проставлен тип, int], int] . Здесь [int, int] — это типы аргументов функции operation , получается, что у этой функции должно быть два аргумента и они оба должны иметь тип int . Последний int в определении типа Callable[[int, int], обозначает тип возвращаемого функцией значения файлы и работа с нетипизированными библиотеками Важно понимать, что type hinting работает не только для аргументов функций и возвращаемых значений. Мы можем просто создать переменную и указать ей тип: В таком сценарии это избыточно — IDE и статический анализатор кода итак видят, что в переменной book хранится значение типа str . Однако в таком сценарии get_user_phone ( user : User ) -> Phone : return user phone from typing import Callable def mysum ( a : int , b : int ) -> int : return a + b def process_operation ( operation : Callable [[ int , int ], int ], a : int , b : int ) -> int : return operation ( a , b ) ( process_operation ( mysum , 1 , 5 )) # Тополек мой в красной косынке функция поиска книги find_book_in_library может быть не нашей функцией, а функцией какой-то внешней библиотеки, которая не использует подсказки типов. То есть для функции может быть не проставлен тип возвращаемого значения. Чтобы и статический анализатор знали, что тип данных, который будет храниться в это именно str , можно таким образом подсказать инструментам о верном типе. Иногда это бывает очень полезно, когда библиотека не использует подсказки типов, а возвращаемый тип данных какой-то сложный и мы хотим, чтобы IDE и mypy нам помогали анализировать наш код и типы. В тоже время в Python существует механизм так называемых стаб-файлов, которые позволяют типизировать в том числе внешние библиотеки. Например, для django есть пакет в pip, который называется django-stubs . О стаб-файлах есть видео на канале Диджитализируй!. Если вы используете нетипизированную библиотеку — можно поискать готовые стаб файлы для не, чтобы воспользоваться преимуществами типизированного Подсказки типов нужны только в функциях? В чате Telegram канала задали отличный вопрос — подсказки типов имеет смысл ставить только для аргументов функций и возвращаемых значений или вообще для всех переменных? И действительно. Как лучше? В большинстве сценариев подсказок типов достаточно только для аргументов и результатов функций. Если в нашем коде все функции типизированы таким образом, то получается, что IDE и статический анализатор кода понимают тип любой переменной в коде и могут выполнять все проверки. Объявляя переменную, мы либо задаём её значение в явном виде (и тогда тип переменной равен типу значения): Здесь тип переменной age равен типу значения 33 , то есть int , а тип переменной user равен User book : str = Тополек мой в красной косынке = 33 user = Иннокентий Второй вариант создания переменной — присваивание ей значения, которое возвращается функцией: Если все функции в нашем коде типизированы, в том числе и функция get_user_by_username , то ив таких сценариях тип переменной очевиден. Какой тип данных функция возвращает, такой тип данных у переменной и будет. Получается, что если все функции, используемые в коде, типизированы, то как правило нет смысла проставлять типы для обычных переменных. Однако, иногда бывает так, что мы используем внешнюю библиотеку, функции которой нетипизированы. Если функция get_user_by_username в примере выше это функция внешней библиотеки иона нетипизирована, то IDE и статический анализатор кода не знают, какой тип данных вернёт эта функция и потому не знают, какой тип будет у переменной user . Тогда можно подсказать инструментам, явно указав тип: Теперь IDE и статический анализатор будут знать тип переменной и смогут выполнять все проверки. Отлично! Ещё один сценарий, при котором полезно задать переменной тип — когда мы инициализируем переменную пустым значением, но хотим указать, данные какого конкретно типа там будут. Например, в конструкторе класса мы инициализируем атрибут с начальным значением {} , то есть пустой словарь, но указываем, что в этом словаре ключами будут строки, значениями числа. То есть мы уточняем тип данных, сужаем его с просто словаря до словаря с конкретным типом ключей и значений = Иннокентий User = Иннокентий self _some_dict : dict [ str , int ] = {} def some_method ( self ): self _some_dict [ "some_key" ] = 123 # Всё ок по типам self _some_dict [ 123 ] = "some_key" # Ошибка типов Резюме Грамотное использование type hinting и осознанный выбор классов отделяет код новичка от кода растущего профессионала. Пользуйся подсказками типов, продумывай структуру твоего приложения и используемые типы данных, и тогда твои решения будут красивыми, приятно читаемыми, легко поддерживаемыми и надёжными. Ты дочитал досюда и разобрался с материалом Отлично, молодец Думаю, тебе стоит прислать нам резюме на почту join@to.digital Контакты У нас много хороших материалов в YouTube, если вдруг ты ещё не подписан — YouTube канал Диджитализируй! Много оперативной и текстовой полезной информации — в Telegram канале ! Также, конечно, есть VK группа и Дзен Об образовательной программе Диджитализируй! Сейчас готовится к перезапуску курс Основы компьютерных и веб-технологий с. Курс — отличный, на Stepik Awards был признан лучший платным курсом. Подписывайся, чтобы не пропустить старт. Новое издание курса будет на больше и полезнее! А если ты читаешь это позднее июня 2022, то вполне вероятно, что курс уже вышел. Беги по ссылке Здесь приведены редакции этой книги и изменения в каждой. Последнюю, актуальную версию книги можно скачать здесь t.me/t0digital/151 27 мая 2022 1600 Изменения: Обновлена глава о Добавлена информация о pyright анализаторе кода в дополнение к Добавлены рекомендации в главу Контейнеры — Iterable, Sequence, Mapping и другие» Исправлены найденные опечатки мая 2022 0400 Изменения: Добавилась информация о протоколах typing.Protocol в главе об интерфейсах Добавилась информация о параметрах датаклассов slots и Добавлен пример в главу Интерпретатор не проверяет подсказки типов» Расширена глава о Enum , добавлено про наследование от Расширена глава Подсказки типов нужны только в функциях?» Исправлены найденные опечатки мая 2022 Первая версия книги |