3 инструмента для Python, которые упростят работу с кодом
Разбор полезных инструментов для Python с функциями умного ИИ автодополнения, статического и динамического анализа кода.
С опытом количество используемых в работе инструментов сокращается, поскольку многие из них вы просто перерастаете. Но такие, как эти, остаются в использовании надолго.
Инструмент 1: ИИ автодополнение и быстрый доступ к документации с Kite
У современных IDE есть встроенное автозаполнение, которое выглядит примерно так:
Встроенный инструмент обычно использует документацию для определения имён и типов возможных функций и параметров. Но что если редактор получит доступ к коду из всех публичных репозиториев GitHub и начнёт подтягивать не просто имена, а целые строки кода? И это реализовано в Kite.
Прим.ред. Такая функциональность доступна в планах Pro и Team. На момент написания материала для плана Pro действует бесплатная бета-версия.
Разберём подробнее особенности Kite — инструмента для Python с функциями умного автодополнения и быстрого доступа к документации.
Умные подсказки
Плагин Kite смотрит в комплексе ваш код, переменные, часто используемые имена параметров, документацию, и только после сбора всех данных рекомендует что-то вроде этого:
На данный момент этот блок не поддерживается, но мы не забыли о нём! Наша команда уже занята его разработкой, он будет доступен в ближайшее время.
Copilot для документации
Прежде чем беспокоить более опытного коллегу или мчаться за ответами на Stack Overflow, почитайте документацию.
Kite Copilot упрощает поиск по документации. Он работает параллельно с IDE и показывает информацию о любых объектах или функциях, на которые наведён курсор.
Работает локально, приватно
Вдобавок ко всему, плагин создан для локальной работы, так что вы получаете быстрые советы, работа происходит в автономном режиме, и ваш код никогда не будет отправлен в облако.
Это крайне важно для людей с плохим интернет-соединением и тех, кто работает с закрытым исходным кодом.
Всё, что нужно сделать, это загрузить и установить плагин Kite для вашего редактора: есть бесплатная версия и Free Beta на Pro план с умным автозаполнением.
Инструмент 2: Статический анализ кода с Mypy
Python — динамически типизированный язык. Это значит, что переменная связывается с типом данных не в момент объявления, а в момент присваивания ей значения. То есть одна и та же переменная может быть и строкой, и целым числом, и каким-либо другим типом в зависимости от последнего присвоенного ей значения.
# Например, вот одна и та же переменная с разными типами данных. # Python определяет тип данных динамически: # string var_name = "string here" # integer var_name = 1234
А вот примеры языков со статической типизацией, где для каждой переменной задан один конкретный тип данных, и в логике кода нужно придерживаться именно его:
# Для многих языков обязательно предварительное объявление типа. # string str str_var_name = "string here" # integer int int_var_name = 1234
Плюсы и минусы динамической типизации
Главное преимущество динамической типизации в том, что вам может быть лень постоянно прописывать типы, и такой язык, как Python, в этом поможет.
А вот недостатков больше:
- как правило, вы сталкиваетесь с ошибками на более поздних этапах разработки;
- код работает медленнее, ведь Python постоянно вычисляет типы;
- код становится менее безопасным, так как на входе и выходе функции у одной и той же переменной могут быть разные типы данных;
- читать ваш код становится сложнее, поскольку другой человек не может быть уверен в том, что уже объявленная переменная не изменит свой тип в дальнейшем.
Статическая типизация в Python
В Python нет статической типизации как таковой, но есть аннотации типов, которые проверяются статическими анализаторами с целью контроля типов переменных.
Обратите внимание на Mypy. Это статический анализатор типов для Python, который позволяет находить ошибки несоответствия типов в коде. Mypy выводит ошибку, если при работе с аннотациями типов значение переменной не соответствует присвоенному ей типу.
from typing import Iterator def fib(n: int) -> Iterator[int]: a, b = 0, 1 while a < n: yield a a, b = b, a + b fib(10) fib("10")
В результате запуска статического анализатора Mypy в приведённом выше коде вы получите следующую ошибку: main.py:10: error: Argument 1 to "fib" has incompatible type "str"; expected "int" . Таким образом Mypy предупреждает, что мы пытаемся присвоить строковое значение переменной с целочисленным типом данных. Это лишь один из примеров использования Mypy. Все функциональные возможности инструмента, более подробно описанные в документации, можно протестировать в песочнице.
Если ваш код работает на проде, и вам важна его устойчивость к ошибкам, связанным с типизацией, используйте инструмент Mypy.
Инструмент 3: Быстрый поиск ошибок и чистый код с SonarLint
Сейчас почти во всех IDE есть линтер — статический анализатор возможных ошибок. Другими словами, он предугадывает ещё до запуска кода, что может пойти не так, и выделяет предполагаемые ошибки.
На данный момент этот блок не поддерживается, но мы не забыли о нём! Наша команда уже занята его разработкой, он будет доступен в ближайшее время.
В свою очередь, динамический анализ не предугадывает, а действительно запускает/компилирует части кода, чтобы определить, работает ли он, но делает это автоматически в фоновом режиме. То есть он на самом деле знает, что пойдёт не так в процессе выполнения программы.
SonarLint — это тот самый динамический анализатор кода, который поможет решить следующие проблемы.
Лишний код
Допустим, вы не удалили закомментированные части кода, оставили неиспользуемые функции и прочие рудименты. SonarLint предупредит об этом, и вы сможете своевременно очистить код от всего лишнего.
Уязвимости
Большая обновляемая база данных с перечнем уязвимостей позволяет плагину вовремя предупреждать о любых известных уязвимостях, которые встречаются в вашем коде.
Когнитивная сложность
Подробнее об этом можно прочесть в статье. Если говорить кратко, разработчики плагина создали математическую формулу, которая может оценить, насколько читабелен ваш код.
Это не только полезно, но и просто. Каждый раз, когда SonarLint предупреждает вас о чрезмерной сложности, это сопровождается объяснением правила, которое вы нарушили. Например, «слишком большая вложенность операторов if», более известная как спагетти-код. Удобно, правда?
Плагин SonarLint позволяет использовать лучшие практики и писать понятный чистый код.
Подытожим
- ИИ автодополнение и быстрый доступ к документации с Kite Copilot и плагином для IDE.
- Статическая типизация кода с модулем Mypy.
- Быстрый поиск ошибок и чистый код с плагином SonarLint.
Все перечисленные инструменты для Python бесплатны или же поставляются в нескольких вариантах, включающих бесплатный.

Следите за новыми постами по любимым темам
Подпишитесь на интересующие вас теги, чтобы следить за новыми постами и быть в курсе событий.
Глубокое понимание аннотаций типов в Python, часть 1

Динамически типизированные языки отлично подходят для быстрого создания прототипов, но по мере роста кодовой базы возрастает риск ошибок типов. Чтобы уменьшить количество таких ошибок, в Python 3.5 появились подсказки типов, которые можно добавлять в код с помощью аннотаций типов, введенных в Python 3.0.
Ефективний курс від robotdreams: Software Architect.
Від ідеї до коду.
С помощью подсказок типов можно аннотировать переменные и функции типами. Python не проверяет типы во время выполнения; вместо этого инструменты статической проверки типов, такие как mypy , pyright или IDE, проверяют на соответствие типы и выдают предупреждения, когда типы используются несогласованно.

Использование статических средств проверки типов имеет множество преимуществ:
- Обнаружение ошибок типов.
- Предотвращение ошибок.
Професійний курс від mate.academy: Java.
Погрузьтеся у світ програмування.
Статическая типизация в Python необязательна и может вводиться постепенно (это известно как постепенная типизация). При постепенной типизации статические средства проверки типов не выдают предупреждений на код без подсказок типов, также не предотвращают компиляцию несогласованных типов во время выполнения.
В этом уроке вы узнаете, как добавить подсказки типов в Python и как использовать mypy для статической проверки типов. Вы будете аннотировать переменные, функции, списки, словари и кортежи. Вы также узнаете, как использовать протоколы, перегрузку функций и аннотирование констант.

Прежде чем начать
Чтобы получить максимальную пользу от этого руководства, у вас должны быть:
- Установленный Python ≥3.10.
- Знания о том, как писать функции, f-строки и выполнять код Python.
- Знать, как использовать командную строку.
Ефективний курс від laba: Фінансовий менеджер.
Оптимізуйте фінансовий розвиток компанії.
Мы рекомендуем Python ≥3.10, так как в этой версии есть новые и лучшие возможности подсказки типов. Если вы используете Python ≤3.9, Python предоставляет альтернативный синтаксис подсказки типов; мы упомянем о нем в учебнике.
Что такое mypy?
mypy — это необязательная статическая проверка типов, созданная Юккой Лехтосало. Он проверяет аннотированный код в Python и выдает предупреждения, если аннотированные типы используются непоследовательно. mypy также проверяет синтаксис кода и выдает синтаксические ошибки, если встречает неправильный синтаксис.
mypy поддерживает постепенную типизацию, позволяя вам добавлять подсказки типов в код медленно, в своем собственном темпе.

Что такое сильная динамическая типизация?
Python — это язык с сильной динамической типизацией. Сильная типизация означает, что она имеет строгие правила типизации и не приводит к неожиданным результатам.
Например, в Python вы не можете добавить целое число к строке, иначе возникнет ошибка типа:
>>> 3 + "hello" TypeError: unsupported operand type(s) for +: 'int' and 'str'
Для сравнения, язык со слабой типизацией, такой как JavaScript, выполнит неявное преобразование типов, и выполнение операции 3 + “hello” приведет к корректному результату:
> 3 + "hello"; "3hello"; // вывод
Динамическая типизация в Python означает, что интерпретатор проверяет типы только во время выполнения программы, и вы можете в любой момент изменить тип переменной:
>>> x = "hello" >>> type(x) # тип переменной — строка >>> x = 3 >>> type(x) # тип переменной изменен на целое число
Что такое статическая проверка типов?
В статически типизированных языках, таких как C и Java, когда вы присваиваете переменной тип, вы не можете изменить тип во время выполнения. Например, если вы объявите переменную как целое число, вы не сможете изменить ее на строку.
int x = 4; x = "hello"; // это вызовет ошибку типа.
В статически типизированных языках компилятор проверяет типы после написания кода. Если ошибок не обнаружено, программу можно запускать.

Все является объектами Python
В Python все является объектами. Определяете ли вы строку, целое число, float , список или словарь — все они являются объектами.
Практичний курс від laba: Директор з продажу.
Створюйте та розвивайте успішний відділ продажів.
Мы можем убедиться в этом, проверив метод isinstance :
>>> isinstance(str, object) True >>> isinstance(int, object) True
Как видите, и строка ( str ), и целое число ( int ) подтвердили, что являются экземплярами объекта.
Поскольку все является объектом, все также имеет атрибуты и методы. Например, если вы определите целое число, вы можете увидеть все его атрибуты, используя dir :
>>> x = 8 >>> dir(x) [. '__add__', '__mul__', '__str__', '__divmod_', ''real', . ] # отредактировано для краткости
Методы с двойным ведущим и последующим подчеркиванием показывают операции, которые поддерживает тип int . Например, метод __mul__ выполняет операцию умножения.
>>> x = 8 >>> x.__mul__(2) 16
По ходу статьи вы узнаете, что объекты могут быть сгруппированы в определенные подсказки типов из-за методов, которыми они обладают.
Например, объекты относятся к типу Sequence , если у них есть методы __getitem__ и __len__ . Примерами таких объектов являются список, строка, кортеж (вывод отредактирован для краткости):
>>> dir([2,3]) # list [. '__getitem__', '__len__'. ] >>> dir((3,2)) # кортеж [. '__getitem__', '__len__'. ] >>> dir('hello') # строка [. '__get_item__', '__len__'. ]
Можете не беспокоиться об этом, если вы не понимаете прямо сейчас — мы рассмотрим тип Sequence в последующих разделах более подробно. Но вы должны понять, что все является объектом и имеет методы. Эти методы используются для группировки определенных объектов в определенные типы.

Исходя из этого, мы начнем аннотировать переменные в следующем разделе.
Добавление подсказок типов к переменным
В этом разделе вы узнаете, как добавлять подсказки типов к переменным.
В Python вы можете определить переменную с подсказкой типа, используя следующий синтаксис:
имя_переменной: тип = значение
Рассмотрим следующую переменную:
Мы присваиваем переменной name строковое значение "rocket" .
Чтобы аннотировать переменную, нам нужно добавить двоеточие ( : ) после имени переменной и объявить тип str :
name: str = "rocket"
В Python подсказки типов, определенных для переменных, можно прочитать с помощью словаря __annotations__ :
>>> name: str = "rocket" >>> __annotations__ >
Словарь __annotations__ покажет вам подсказки типов для всех глобальных переменных.
Теперь вы добавили подсказку типа str к переменной. Как уже упоминалось ранее, интерпретатор Python не принудительно определяет типы, поэтому определение переменной с неправильным типом не вызовет ошибки:
>>> name: int = "rocket" >>>
С другой стороны, статическая проверка типов, например mypy , отметит это как ошибку:
error: Incompatible types in assignment (expression has type "str", variable has type "int")
Объявление подсказок типов для других типов данных соответствует тому же синтаксису. Ниже перечислены некоторые простые типы, которые можно использовать для аннотации переменных:
- float: плавающие значения, например, 3.10
- int: целые числа, такие как 3, 7
- str: строки, например, ‘hello’
- bool: булево значение, которое может быть True или False
- bytes: представляет байтовые значения, например, b’hello’.
Когда вы объявляете переменную с простым типом, таким как int или str, mypy может определить тип. Поэтому в большинстве случаев аннотирование переменных этими простыми типами не требуется.
Однако mypy с трудом определяет типы переменных, хранящих сложные структуры, такие как списки, словари или кортежи. И именно здесь подсказки типов для переменных становятся более важными.
Далее вы узнаете, как добавить подсказки типов к этим структурам, а пока мы добавим подсказки типов к функциям.
Добавление подсказок типов в функции
Теперь мы добавим подсказки типов в функции. Чтобы аннотировать функцию, мы аннотируем каждый параметр и возвращаемое значение:
def function_name(param1: param1_type, param2: param2_type) -> return_type:
Давайте аннотируем следующую функцию, которая возвращает сообщение:
def announcement(language, version): return f" has been released" announcement("Python", 3.10)
Функция принимает строку в качестве первого параметра и float в качестве второго параметра. Затем она возвращает строку как результат.
Чтобы аннотировать параметры функции, мы добавим двоеточие( : ) после каждого параметра, а затем укажем тип параметра:
- язык: str.
- версия: float.
Чтобы указать тип возвращаемого значения, добавим -> перед двоеточием( : ) в определении функции. Это будет выглядеть следующим образом:
def announcement(language: str, version: float) -> str: .
Теперь функция имеет подсказки типа, показывающие, что она принимает аргументы str и float , а возвращает str .
Когда вы вызовете функцию, вы получите:
result = announcement("Python", 4.11) print(result) # Python 4.11 был выпущен
Хотя в нашем коде есть подсказки типов, интерпретатор Python не выдаст предупреждения, если мы вызовем функцию с неправильными аргументами:
result = announcement(True, "Python") print(result) # True Python был выпущен
Функция выполняется успешно, несмотря на то, что в качестве первого аргумента мы передали булево True , а в качестве второго — строку “Python”. Чтобы получать предупреждения об этих ошибках, нам нужно использовать статическую проверку типов, например mypy .
Статическая проверка типов с помощью mypy
Сейчас мы покажем, как проводить статическую проверку типов с помощью mypy , чтобы получать предупреждения об ошибках типа в нашем коде.
Создайте каталог type_hints и переместите его в каталог:
mkdir type_hints && cd type_hints
Создайте и активируйте виртуальную среду:
python3.10 -m venv venv source venv/bin/activate
Установите последнюю версию mypy с помощью pip :
pip install mypy
После установки mypy создайте файл announcement.py и введите в него следующий код:
def announcement(language, version): return f" has been released" announcement("Python", 3.10)
Сохраните файл и выйдите. Мы собираемся повторно использовать ту же функцию из предыдущего раздела.
Далее запустите файл с помощью mypy :
mypy announcement.py Success: no issues found in 1 source file
Как вы можете видеть, mypy не выдает никаких предупреждений. Статическая типизация в Python необязательна, и при постепенной типизации вы не должны получать никаких предупреждений, если только вы не сделаете выбор, добавив подсказки типов в функции. Это позволяет вам медленно аннотировать ваш код.

Давайте теперь разберемся, почему mypy не показывает нам никаких предупреждений.
Тип Any
Как мы уже отмечали, mypy игнорирует код без подсказок типов. Это происходит потому, что он предполагает тип Any в коде без подсказок.
Ниже показано, как mypy видит функцию:
def announcement(language: Any, version: Any) -> Any: return f" has been released" announcement("Python", 3.10)
Тип Any — это динамический тип, который совместим с любым типом. Поэтому mypy не будет жаловаться на то, являются ли типы аргументов функции bool, int, bytes и т.д.
Теперь, когда мы знаем, почему mypy не всегда выдает предупреждения, давайте настроим его для этого.
Настройка mypy для проверки типов
mypy может быть настроен в соответствии с вашим рабочим процессом и практикой работы с кодом. Вы можете запустить mypy в строгом режиме, используя опцию --strict , чтобы отметить любой код без подсказок типов:
mypy --strict announcement.py announcement.py:1: error: Function is missing a type annotation announcement.py:4: error: Call to untyped function "print_release" in typed context Found 2 errors in 1 file (checked 1 source file)
Опция --strict является самой ограничительной и не поддерживает постепенную типизацию. В большинстве случаев нам не нужно быть настолько строгими. Вместо этого мы хотим использовать постепенную типизацию, чтобы добавлять подсказки типов поэтапно.
mypy также предоставляет опцию --disallow-incomplete-defs . Эта опция помечает функции, у которых не аннотированы все параметры и возвращаемые значения. Эта опция очень удобна, потому что если вы забыли аннотировать возвращаемое значение или вновь добавленный параметр, mypy предупредит вас об этом.
Чтобы понять это, давайте добавим подсказки типов только к параметрам и опустим типы возвращаемых значений (представим, что мы забыли):
def announcement(language: str, version: float): return f" has been released" announcement("Python", 3.10)
Запустите файл с помощью mypy без каких-либо опций командной строки:
mypy announcement.py Success: no issues found in 1 source file
Как видите, mypy не предупреждает нас о том, что мы забыли указать тип возврата. Он предполагает тип Any в возвращаемом значении. Если бы функция была большой, было бы трудно определить тип возвращаемого значения. Чтобы узнать тип, нам пришлось бы исследовать возвращаемое значение, что отнимает много времени.
Чтобы защитить себя от этих проблем, мы передадим опцию --disallow-incomplete-defs в mypy :
mypy --disallow-incomplete-defs announcement.py announcement.py:1: error: Function is missing a return type annotation Found 1 error in 1 file (checked 1 source file
Теперь mypy предупреждает нас, что мы пропустили аннотацию возвращаемого типа, поэтому давайте добавим ее:
def announcement(language: str, version: float) -> str: .
Запустите файл снова с включенной опцией --disallow-incomplete-defs :
mypy --disallow-incomplete-defs announcement.py Success: no issues found in 1 source file
На этот раз mypy работает без проблем, потому что мы закончили аннотирование определений функций.
Давайте теперь вернемся к тому, что произошло в предыдущем разделе, когда мы только учились добавлять подсказки типов к функциям. Мы отметили, что в Python нет принудительного указания типов, и что следующий код будет выполняться без проблем:
ef announcement(language: str, version: float) -> str: return f" has been released" announcement(True, "Python") # bad arguments
Давайте посмотрим, будет ли mypy теперь предупреждать нас об этом:
mypy --disallow-incomplete-defs announcement.py announcement.py:4: error: Argument 1 to "print_release" has incompatible type "bool"; expected "str" announcement.py:4: error: Argument 2 to "print_release" has incompatible type "str"; expected "float" Found 2 errors in 1 file (checked 1 source file)
Отлично! mypy предупреждает нас, что мы передали в функцию неправильные аргументы.
Теперь давайте избавимся от необходимости набирать mypy с опцией — disallow-incomplete-defs .
mypy позволяет сохранять опции в файле mypy.ini . При запуске mypy будет проверять этот файл и запускаться с опциями, сохраненными в файле.
Создайте файл mypy.ini в корневом каталоге проекта и введите следующий код:
[mypy] python_version = 3.10 disallow_incomplete_defs = True
В файле mypy.ini мы сообщаем mypy , что мы используем Python 3.10 и что мы хотим запретить неполные определения функций.
Сохраните файл в своем проекте, и в следующий раз вы сможете запустить mypy без каких-либо опций командной строки:
mypy announcement.py Success: no issues found in 1 source file
У mypy есть множество опций, которые вы можете добавить в файл mypy . Я рекомендую обратиться к документации по командной строке mypy , чтобы узнать больше.
Теперь вы узнали, как выполнять статическую проверку типов с помощью mypy . Далее мы добавим подсказки типов в функцию без оператора возврата.
Добавление подсказок типов в функции без оператора возврата
Не все функции имеют оператор возврата. Когда вы создаете функцию без оператора возврата, она все равно возвращает значение None :
def announcement(language: str, version: float): print(f" has been released") result = announcement("Python", 4.11) print(result) # None
Значение None не является полезным, поскольку оно не предназначено для использования. Оно лишь показывает, что функция выполнилась успешно.
Чтобы показать, что функция ничего не возвращает, мы можем аннотировать возвращаемое значение функции значением None :
def announcement(language: str, version: float) -> None: .
Добавление подсказок о типе объединения в параметрах функции
Когда функция принимает параметр более чем одного типа, вы можете использовать символ объединения ( | ) для разделения типов.
Например, следующая функция принимает параметр, который может быть либо str , либо int :
def show_type(num): if(isinstance(num, str)): print("You entered a string") elif (isinstance(num, int)): print("You entered an integer") show_type('hello') # You entered a string show_type(3) # You entered an integer
Функция show_type может быть вызвана со строкой или целым числом, и будет выводить результат в зависимости от типа значения, переданного в качестве аргумента.
Для аннотации параметра мы будем использовать символ объединения | , который был введен в Python 3.10, для разделения типов следующим образом:
def show_type(num: str | int) -> None: . show_type('hello') show_type(3)
Союз | теперь показывает, что параметр num является либо str , либо int .
Если вы используете Python ≤3.9, вам необходимо импортировать Union из модуля типизации. Параметр может быть аннотирован следующим образом:
from typing import Union def show_type(num: Union[str, int]) -> None: .
Добавление подсказок о типе к необязательным параметрам функции
Не все параметры в функции обязательны; некоторые из них необязательны. Пример функции, принимающей необязательный параметр, приведен ниже:
def format_name(name: str, title = None) -> str: if title: return f"Name: . " else: return f"Name: " format_name("john doe", "Mr")
Второй аргумент title является необязательным параметром. Необязательным его делает установка параметра title в значение по умолчанию — None , в данном случае. Параметр может принимать строку, а может принимать значение по умолчанию None , если аргумент не указан.
Чтобы указать, что title является необязательным, мы воспользуемся типом Optional[str] из модуля typing :
from typing import Optional def format_name(name: str, title: Optional[str] = None) -> str: . format_name("john doe", "Mr")
Теперь мы аннотировали необязательный параметр title типом Optional . В следующем разделе мы добавим подсказки типов к спискам.
Конец первой части
В этой вводной части мы рассмотрели основы аннотирования типов и связанной с ним теории и инструментов. Приглашаем прочитать эту вторую часть, где мы рассмотрим более продвинутые примеры работы с аннотациями в режиме объектно-ориентированного программирования.
Статья mypy: основы и немного больше
Статический анализатор типов mypy предотвращает значительное количество возможных ошибок в коде на языке программирования Python. Главная его цель – предоставить возможность писать более надежный и легко поддерживаемый код.
def greet(name: str) -> str: return f"Hello, !" def add_numbers(x: int, y: int) -> int: return x + y def get_length(s: str) -> int: return len(s) def is_even(n: int) -> bool: return n % 2 == 0
- Устанавливаем mypy командой pip install mypy ;
- Сохраняем код в файл, для примера, в mpy.py ;
- Выполняем команду mypy mpy.py .
Если же заменить тип возвращаемого значения greet на int, сделав программу некорректной, mypy выдаст следующую ошибку:
mpy.py:2: error: Incompatible return value type (got "str", expected "int") [return-value] Found 1 error in 1 file (checked 1 source file)
Стандартные типы
- int – целочисленная переменная любой длины. Например: 42, -10, 0;
- float – число с плавающей точкой. Например: 0.13, 5.0, math.pi;
- bool – булево значение, True или False;
- str – строка. Например: “Hello, world!”;
- bytes – последовательность байт. Например: b”Hello”;
- bytearray – изменяемый массив байт. Например: bytearray(b”Hello”);
- None – аналог void типа в Си. Имеет None как единственное значение.
- List[T] – список, элементы которого имеют тип T;
- Tuple[T1, T2, …, TN] – кортеж, первый элемент которого имеет тип T1, второго – T2 и так далее;
- Dict[K, V] – словарь, ключи которого имеют тип K, а значения – тип V;
- Optional[T, V] – тип T или None, аналогично Union[T, None];
- Any – любой тип;
- Union[T1, T2, …, TN] – любой тип из T1, T2, …, TN;
- Callable[[T1, …], TResult] – функция, которая принимает аргументы типов T1, … и возвращает TResult.
from typing import List def rot13(b: bytes) -> bytes: result: List[int] = [] for byte in b: if 65 str: b: bytes = s.encode('ascii') roted: bytes = rot13(b) return roted.decode('ascii') print(crypt("Hello, world!"))
Используя тип Union можно переписать в одну функцию следующим образом:
from typing import List, Union def rot13(data: Union[bytes, str]) -> Union[bytes, str]: if isinstance(data, str): roted = rot13(data.encode('ascii')) if isinstance(roted, bytes): return roted.decode('ascii') else: return roted else: result: List[int] = [] for byte in data: if 65
Вывод такой же, “Uryyb, jbeyq!”. Но, как видно в коде, нам приходится явно проверять условие, что возвращаемое значение для байтов имеет тип bytes, а не str, хотя иначе, согласно нашему алгоритму, быть не может.
Проблему из предыдущего примера можно решить, используя перегрузки:
from typing import List, Union, overload @overload def rot13(data: bytes) -> bytes: . @overload def rot13(data: str) -> str: . def rot13(data: Union[bytes, str]) -> Union[bytes, str]: if isinstance(data, str): return rot13(data.encode('ascii')).decode('ascii') else: result: List[int] = [] for byte in data: if 65
Для перегрузок мы не указываем реализацию, вместо нее требуется писать многоточие. В финальной реализации мы можем обращаться к функции, предполагая, что ее возможные типы соответствуют перегрузкам. Это позволяет более точно обозначить зависимости между типами аргументов и типом результата. Однако, в реализации все равно придется писать Union’ы.
У mypy имеется некоторое количество опций, полный список которых можно посмотреть в официальной документации (
Ссылка скрыта от гостей
). Они могут быть полезны при разработке, так как позволяют задать "строгость" проверки.
Опции могут быть включены через явный параметр (пример: --strict ), но удобнее содержать их в файлах. Mypy поддерживает следующие файлы конфигурации: mypy.ini , .mypy.ini , pyproject.toml и setup.cfg . Изначально он будет ожидать один из этих файлов в текущей директории.
Пример mypy.ini файла:
[mypy] disallow_untyped_defs=True no_implicit_optional=True warn_return_any=True
- disallow_untyped_defs – запрещает объявлять функции без аннотации типов;
- no_implicit_optional – без этой опции аргументы функции могут иметь тип None в качестве параметра по-умолчанию. С этой опцией такое неявное поведение запрещено, а гарантии типов сохранены;
- warn_return_any – название говорит само за себя. С этой опцией mypy предупреждает, если в качестве возвращаемого типа было указано Any. Чаще всего Any ставится как временная заглушка, так что mypy поможет вам не забыть поменять заглушку на действительный тип.
- check_untyped_defs – проверяет корректность типизации в функции без аннотаций. По-умолчанию mypy закрывает глаза на все, что происходит внутри таких функций. С этой опцией он проверяет корректность типизации всех операций и вызовов внутри;
- strict – включает все опциональные дополнительные проверки;
- warn_unreachable – сообщает, если какой-то кусок кода никогда не выполнается. Если так случилось, вероятно была допущена ошибка в проверке условий.
Заключение
Mypy очень прост в изучение и использовании. Несмотря на простоту, он позволяет явным образом прописать используемые в проекте типы, предотвращая множество ошибок. Если вы случайно передали в функцию не тот аргумент, без mypy есть риск узнать об этом уже на стадии выполнения, возможно даже в продакшене.
Кроме того, для интеграции mypy с существующими средствами разработки уже существуют готовые плагины. Для PyCharm и VisualStudio Code они так и называются, их легко найти в маркетплейсе. Mypy может быть легко интегрирован в CI/CD и использоваться при сборке проекта.
Используйте статическую типизацию всегда, когда возможно – это довольно легко упростит вам жизнь с современными технологиями.
Статические анализаторы mypy и pyright
mypy это инструмент, который устанавливается отдельно как pip-пакет и запускается в проекте как часть тестов или CI/CD процесса. Перед сборкой и раскаткой приложения на сервер запускается проверка исходного Python-кода с mypy и если mypy находит ошибки, то процесс останавливается, разработчики исправляют найденные ошибки и процесс повторяется. Это приводит к тому, что до продакшн, то есть до рантайма и до живых пользователей соответственно ошибок долетает меньше, потому что многое выявляется на более ранних этапах.
В директории проекта создадим и активируем виртуальное окружение, установим в него mypy и запустим проверку нашего кода:
python3.10 -m venv env . ./env/bin/activate pip install mypy mypy ./weather

Как видим, mypy не нашёл проблем в нашем коде. Внесём специально ошибку в код и убедимся, что mypy её найдёт:

Запуск mypy можно встроить в процесс CI/CD, чтобы процесс разворачивания приложения на серверах не запускался, если проверки mypy не прошли. Таким образом до runtime не смогут дойти ошибки, связанные с некорректным использованием типов данных, и это здорово — надёжность приложения значительно возрастает!
И ещё важно отметить, что используя mypy, вы можете проверять корректность своих тайп-хинтингов, которые вы указали. Пока учишься могут быть вопросы, правильно ли указан тип — вот можно указать тип у параметра функции, вызвать эту функцию с данными и посмотреть, как поведёт себя проверятор типов, встроенный в IDE, и как поведёт себя mypy .
Помимо mypy пользуется популярностью анализатор pyright . Они работают по-разному, например, такой код валиден с точки зрения mypy (и с точки зрения анализатора, встроенного в PyCharm IDE), но невалиден с точки зрения pyright :
class User: def __init__(self): self.name: str = "Petr" def yo(self): self.name = <>
Анализаторы кода продолжают развиваться и дорабатываться, они неидеальны, но проставлять подсказки типов — обязательное условие для серьёзных проектов. Анализаторы кода достаточно умны уже сейчас и станут ещё умнее в будущем.