Если вы работаете над большим проектом на Python или предпочитаете лаконичную и понятную базу кода, то Pytype станет для вас отличным помощником.
Python — это отличный язык программирования для создания скриптов и прототипов. Емкий синтаксис, гибкая система типов и хорошая интерпретируемость помогают быстро опробовать идею, скорректировать ее и попробовать еще раз.
В процессе роста проекта гибкость, которая когда-то обеспечивала быстроту работы, становится тяжким бременем, замедляющим разработку. Когда к проекту подключаются другие разработчики, пишется больше кода. А нехватка информации по типам осложняет чтение и понимание кода. Без системы проверки типов легко допустить ошибки. Но вот отловить их будет не так-то просто.
Pytype спешит на помощь! Pytype — это open-source инструмент для проверки и вывода типов в Python. К тому же, это уже готовое решение — установите и приступайте к работе!
Pytype может:
- Статистически выводить информацию о типах и проверять код на ошибки в типах.
- Проверять согласованность аннотаций типов PEP 484.
- При необходимости возвращать выведенную информацию о типах.
Если вы уже заинтересовались, то установите Pytype и следуйте инструкциям. Ниже приведены полезные примеры использования.
Пример 1: Вывод и проверка типа
Это самый популярный сценарий. Вы пишите код и хотите проверить его на наличие ошибок. Присмотритесь к этой функции:
import re
def GetUsername(email_address):
match = re.match(r'([^@]+)@example\.com', email_address)
return match.group(1)
Тут все понятно. Она использует регулярное выражение для извлечения части адреса электронной почты до @ и возвращает его. А вы заметили ошибку?
Давайте посмотрим, что нам скажет pytype
:
% pytype get_username.py
Analyzing 1 sources with 0 dependencies
File "/.../get_username.py", line 5, in GetUsername: No attribute 'group' on None [attribute-error]
In Optional[Match[str]]
Pytype говорит нам, что group
не является допустимым вызовом функции для match
. При отсутствии совпадений re.match()
возвращает None
. И действительно, в данных случаях match.group(1)
выбросит исключение.
Давайте устраним ошибку, сделав так, чтобы для некорректного адреса почты функция возвращала None
:
import re
def GetUsername(email_address):
match = re.match(r'([^@]+)@example\.com', email_address)
if match is None:
return None
return match.group(1) # <-- Here, match can't be None
Теперь, когда мы вновь запустим pytype
, ошибка не обнаружится. Pytype предполагает, что при выполнении кода после if, match
точно не окажется None
.
Пример 2: Проверка аннотации типов
В Python 3 вам доступна аннотация типов (PEP 484). Так вы сможете лучше донести свою мысль до средств проверки типов и других разработчиков. Pytype предупреждает об ошибках в аннотации типов:
import re
from typing import Match
def GetEmailMatch(email) -> Match:
return re.match(r'([^@]+)@example\.com', email)
Давайте проверим этот фрагмент кода через pytype
:
% pytype example.py
Analyzing 1 sources with 0 dependencies
File "/.../example.py", line 5, in GetEmailMatch: bad option in return type [bad-return-type]
Expected: Match
Actually returned: None
Pytype сообщает, что GetEmailMatch
может вернуть None
. Но мы задали его возвращаемый тип как Match
. Для исправления ошибки можно прибегнуть к аннотации типа Optional
из модуля typing
:
import re
from typing import Match, Optional
def GetEmailMatch(email) -> Optional[Match]:
return re.match(r'([^@]+)@example\.com', email)
Optional
означает, что возвращаемое значение может быть объектом Match
или None
.
Пример 3: Возвращение информации о выведенных типах
Чтобы лучше освоиться в аннотациях типов, Pytype может добавлять их в сам код. Давайте рассмотрим этот фрагмент:
import re
def GetEmailMatch(email):
return re.match(r'([^@]+)@example\.com', email)
def GetUsername(email_address):
match = GetEmailMatch(email_address)
if match is None:
return None
return match.group(1)
Перед добавлением аннотации типов сначала запускаем pytype
для файла. pytype
сохраняет информацию о выведенном типе в файле.pyi
. Затем мы можем вернуть аннотации типов в код через запуск merge-pyi
:
% pytype email.py
% merge-pyi -i email.py pytype_output/email.pyi
Вуаля!
import re
from typing import Match
from typing import Optional
def GetEmailMatch(email) -> Optional[Match[str]]:
return re.match(r'([^@]+)@example\.com', email)
def GetUsername(email_address) -> Optional[str]:
match = GetEmailMatch(email_address)
if match is None:
return None
return match.group(1)
Аннотации типов, включая выражения import
, теперь присутствуют в исходном файле.
Перевод статьи Ehud Tamir: How to quickly find type-issues in your Python code with Pytype