Использование стандартной библиотеки для преобразования сигнатуры функции в парсер аргументов консоли

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

Вам нужно как-то параметризовать свои скрипты, то есть настроить папки ввода / вывода, настроить параметры обучения или заменить одну архитектуру на другую. Для этой цели вы должны написать правильный анализатор аргументов, который преобразует ваши параметры интерфейса командной строки в переменные сценария или параметры функции. Поэтому каждый раз, когда у вас есть какой-то фрагмент, вы пишете много повторяющегося шаблонного кода, который не будет жить долго. Было бы здорово, если бы вы могли легко преобразовать произвольную функцию Python в инструмент командной строки, не так ли?

Готов поспорить, что сейчас многие читатели скажут, что существует отличная Fire библиотека, которая преобразует произвольную функцию или класс Python в готовый для консоли скрипт. Однако мне это кажется немного громоздким, и я делаю много вещей, которые мне не нужны. Поэтому мне было интересно, насколько сложно может быть написать решение «сделай сам», менее мощное, но простое и лучше адаптированное к моим потребностям? Ответ: совсем не сложно!

В этом посте я собираюсь показать, как можно использовать модуль Python inspect для динамического создания ArgumentParser экземпляров, используя менее нескольких десятков строк кода.

TL; DR: Вот ссылка на репозиторий, которая включает немного расширенную версию кода, описанного в этом посте, и позволяет создавать специальные утилиты CLI из любой функции Python с помощью команды python -m ancli path.to.module:function.

Постановка цели

Рассмотрим следующий фрагмент, который просматривает папку и объединяет ее содержимое в один файл.

Представим, что вы хотите преобразовать его в сниппет, который можно было бы запускать как консольную программу. Вы хотели бы сделать это с минимальными усилиями. Вы можете использовать argparse и добавлять каждый параметр вручную, но это будет очень утомительный процесс, в основном связанный с «дублированием» каждого параметра функции в качестве параметра интерфейса командной строки. Должен быть способ получше.

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

Что ж, у нас нет такой функции в стандартной библиотеке, но мы легко можем написать ее сами.

Проверка подписи функции

Для достижения цели, заявленной в предыдущем разделе, нам нужно (а) выяснить, какие аргументы принимает функция, (б) использовать эту информацию для создания экземпляра argparse, который анализирует ввод консоли, и (в) вызвать функцию с проанализированными параметрами. .

К счастью, у нас есть встроенный модуль inspect, позволяющий читать подпись любого вызываемого Python. Применим его к нашей concat функции.

>>> import inspect
>>> sig = inspect.signature(concat)
>>> sig
<Signature (folder: str, output: str = 'concat.txt', include_names: bool = True)>
>>> for p in sig.parameters.values(): print(p)
folder: str
output: str = 'concat.txt'
include_names: bool = True

Легко! Подпись дает нам доступ к именам параметров, их аннотациям и значениям по умолчанию. Это также помогает различать обязательные и необязательные параметры. По большей части это все, что нам нужно для создания парсера аргументов.

Генерация парсера

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

Прежде всего, мы берем сигнатуру функции, как показано в строке 6. Затем мы перебираем его параметры в цикле, объявленном в строках 9-30. Мы используем атрибуты объектов inspect.Parameter для доступа к аннотациям, типам (если есть) и значениям по умолчанию. Если значение по умолчанию недоступно, мы обрабатываем параметр как требуемый. Каждый параметр мы добавляем в парсер. Наконец, мы вызываем синтаксический анализатор и передаем проанализированные значения в функцию. Легкий!

Теперь давайте проверим, как это работает. Приведенный ниже фрагмент включает наши concat и make_cli функции и объединяет их для создания утилиты командной строки.

Вывод

Множество интересных проектов и инструментов, которые используют инспекцию, чтобы упростить процесс разработки. Различные IDE используют его для реализации основных моментов кода и советов. Анализаторы статического типа, такие как mypy, помогают проверить правильность программы Python перед ее запуском. Библиотека sklearn использует проверку для получения / установки параметров оценщика.

Хотя эти примеры не должны создавать впечатление, что возможности Python для отражения являются чем-то «волшебным» и полезны только при разработке сложной библиотеки машинного обучения. Это может быть очень полезный инструмент при работе над повседневными задачами и создании инструментов и приложений широкого спектра, от очень сложных до самых простых.

Вас интересует язык Python? Не можете жить без машинного обучения? Прочитали что-нибудь в Интернете?

Тогда, возможно, вас заинтересует мой блог, где я рассказываю о различных темах программирования и даю ссылки на учебники и руководства, которые мне показались интересными.