с объяснением исходного кода и упрощенной реализацией

Примечание: эта статья является частью моей серии Простые методы и инструменты для науки о данных, которая представляет собой набор дополняющих друг друга мощных инструментов. Эта статья основана на: mypy 0.991 pydantic 1.10.2 beartype 0.11.0.

Здесь будет обсуждаться однострочный декоратор validate_arguments, предоставленный pydantic. Я также объясню исходный код и в конце реализую упрощенную версию!

1. Введите подсказки

Если вы уже знакомы с подсказками типов, пожалуйста, пропустите этот раздел; но если вы этого не сделаете, я объясню это на простом примере.

Большинство статических языков, таких как Java, требуют указания типов аргументов и возвращаемых значений для функций.

Здесь, в addTwoNums функции Java, мы знаем, что два аргумента num1 num2 и возвращаемое значение имеют тип int. В его эквиваленте Python мы можем объявить типы в строке документации или документации.

Однако есть более нативный способ их определения — типовые подсказки.

Как видно из приведенного выше кода, мы можем определить типы аргументов и возврата, используя подсказки типов (: для аргументов и -> для возврата), поэтому нам не нужно снова записывать их в строке документации.

Подсказка типов — это относительно новая функция, которая была представлена ​​в Python 3.5. Ведутся споры о том, следует ли использовать подсказки типов, потому что, в конце концов, Python — это язык с динамической типизацией, а типы никоим образом не применяются принудительно. Лично я считаю хорошей практикой добавлять подсказки типов, так как это не только улучшает читабельность кода и упрощает документирование, но также позволяет выполнять различные проверки, связанные с типами, о чем я расскажу ниже, и я использую подсказки типов в своей работе.

2. Проверяйте типы ввода по подсказкам типов

Есть несколько инструментов, которые позволяют нам проверять код на основе подсказок типов.

2.1. Статическая проверка

Как следует из названия, средство проверки статического типа проверяет ваш код статически, другими словами, оно проверяет входные типы ваших функций, если эти вызовы функций уже существуют в вашей кодовой базе.

MyPy (14,4 тысячи звезд на GitHub), вероятно, самый популярный инструмент такого рода, с некоторыми альтернативами, включая Pyright, Pytype и Pyre.

Например, если у нас есть скрипт, который вызывает функцию с подсказками типа, но имеет недопустимые типы ввода:

Mypy может сканировать сценарии и обнаруживать ошибки типа. Однако, если функция вызывается во время выполнения, mypy не сможет ее проверить.

Для получения дополнительной информации о статической проверке кода и линтинге, пожалуйста, следите за моей серией статей Написание готового к производству кода для науки о данных, где я создам статью на эту тему.

2.2. Средство проверки времени выполнения

Предполагая, что мы разрабатываем пакет Python для команды и хотим проверять типы входных данных для наших функций во время выполнения, когда их используют коллеги, мы можем просто добавить декоратор validate_arguments из Pydantic (12 тысяч звезд на GitHub) в наш функция.

Примеры говорят сами за себя, и мы видим, что декоратор проверяет входные данные на соответствие типам и выдает ValidationError, если типы не совпадают.

Pydantic не единственный, кто предлагает эту функциональность; есть и другие декораторы, достигающие того же, например beartype (1,4 тысячи звезд на GitHub).

Однако сообщения об ошибках BearType явно не так читабельны, как сообщения pydantic.

3. Разрешить произвольные типы с помощью pydantic

По умолчанию декоратор validate_arguments поддерживает только встроенные типы Python, но часто мы можем захотеть передать Pandas DataFrame в качестве аргумента, и мы можем сделать это, разрешив произвольные типы в конфигурации, как показано ниже:

В этом примере функция просто возвращает первые n строк кадра данных, и из примеров видно, что декоратор validate_arguments теперь также может проверять тип pd.DataFrame.

Предупреждение: как указано в официальной документации, декоратор validate_arguments находится в бета-версии и был добавлен в pydantic в v1.5 на временной основе. Он может измениться в будущих версиях, и его интерфейс не будет конкретным до v2.

4. (Необязательно) Как это работает

Если вы являетесь пользователем Python от среднего до продвинутого, то последние два раздела для вас!

Хотя подробное описание исходного кода, безусловно, выходит за рамки этой статьи, давайте рассмотрим две наиболее фундаментальные части головоломки: подсказки типов и пользовательский ввод.

4.1. Подсказки типа доступа из атрибута функции

Если у нас есть функция с подсказками типа, мы можем получить доступ к информации о типе в атрибуте __annotations__ функции. Например:

И это используется в функции _typing_extra.get_type_hints в pydantic.

4.2. Подсказки типа доступа из сигнатуры функции

Мы также можем получить доступ к аннотациям из подписи функции с помощью встроенного в Python модуля проверки. Например:

Это также используется в классе ValidatedFunction в pydantic.

4.3. Доступ к пользовательскому вводу в декораторе

Это будет легко понять, если у вас есть опыт работы с декораторами Python. Короче говоря, декоратор — это (в основном) функция, которая принимает на вход другую функцию, что-то с ней делает, а затем возвращает исходную функцию. Мы можем использовать символ @, чтобы добавить декоратор к функции, но на самом деле это просто означает, что декоратор принимает функцию в качестве входных данных, и два метода ниже (строка 13 и строка 19 — с символом @ или без него) эквивалентны.

В этом простом примере my_decorator выводит сообщение до и после выполнения исходной функции greet. Не вдаваясь в подробности о декораторах, важно сделать вывод, что все аргументы (args и kwargs, в данном примере «Джон») исходной функции также передаются в сам декоратор, и именно так мы можем получить пользователя. входы.

5. (Дополнительно) Давайте реализуем свои собственные!

Изучив основные части декоратора validate_arguments, почему бы нам не реализовать собственную упрощенную версию?

Идеальный! Теперь мы сами реализовали декоратор validate_arguments, и, как видно из предоставленных примеров, он работает именно так, как мы и ожидали. Несколько ключевых моментов здесь:

  • Сначала мы используем signature(func).parameters (строка 9) для получения ожидаемых имен и типов аргументов.
  • Затем мы используем signature(func).bind(*args, **kwargs) (строки 12–14) для чтения всех имен и значений параметров вызова функции, включая аргументы, аргументы ключевых слов и значения по умолчанию.
  • Наконец, проверьте все входные значения на соответствие ожидаемым типам и вызовите исходную функцию, если все типы допустимы, в противном случае вызовите TypeError (строки 19–24).

Вы могли заметить декоратор @wraps из functools (встроенный модуль Python) в строке 6, который также появился на третьем скриншоте исходного кода pydantic выше. По сути, он копирует метаданные (имя, строку документации, аннотации и т. д.) исходной функции в оболочку, чтобы мы могли сохранить информацию в исходной функции после ее оформления, что является хорошей практикой, когда развивающие декораторы. И в строках 62–65 мы видим, что метаданные, например. __doc__, __annotations__ атрибуты украшенной функции get_dataframe_head сохраняются, а не получаются метаданные wrapper.

Конечно, я не ожидаю, что моя упрощенная реализация превзойдет pydantic, которая гораздо более детализирована и протестирована с различными пограничными случаями, но, тем не менее, я надеюсь, что вы нашли это полезным и узнали что-то новое или получили вдохновение. (Тем не менее, наш простой декоратор на самом деле работает довольно хорошо в большинстве случаев, с которыми мы сталкиваемся, но ниже приведен лишь пример тестов пограничного случая — classmethod — которые декоратор pydantic проходит, а наш терпит неудачу.)

Удачного кодирования!

Спасибо, что прочитали, и ваши отзывы более чем приветствуются. Вы также можете подписаться или связаться со мной в LinkedIn, где я создал хэштег #100ArticlesForDS (100 статей для специалистов по данным), чтобы делиться статьями по науке о данных, которые я считаю проницательными и полезными, наряду с моими комментарии, мысли и дополнительные практические советы.