52

Интересный Python #10: CLI-приложения

CLI-приложение (Command Line Interface) - это такое приложение, в котором все действия осуществляются с помощью командной строки, в которую вводятся команды и параметры.

Пример для Windows:

1. Откройте командную строку. Для этого нажмите Win + R, в открывшемся окне напишите cmd и от души приложитесь по кнопке Enter;

2. Введите команду tasklist и нажмите Enter. Вы увидите список запущенных процессов, их идентификаторы и объём памяти, которые они занимают;

3. Выполните команду tasklist /fi "memusage gt 200000", то есть добавьте к tasklist параметр /fi "memusage gt 200000". Вы увидите список процессов, занимающих в памяти более 200 000 килобайт.


Ну а если вы уже знакомы с Git, Docker и другими инструментами, то с их CLI вы наверняка имели дело.


С помощью библиотеки click вы легко сможете создавать свои CLI-приложения. В конце поста, как и всегда, будет тренировочное задание. Приятного чтения :)

Как установить click

1. Откройте терминал (командную строку);

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

3. Выполните команду pip install click.

Документация

У click очень подробная и качественная документация. Ознакомиться с ней можно здесь.

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

Дальше будет много примеров кода. На Пикабу они выкладываются в виде скриншотов. Забрать их в виде текста можно здесь.

Вот так выглядит простейшее консольное приложение на click:

Запустите командную строку и выберите ту директорию, в которой содержится ваш скрипт с расширением .py. Сменить директорию можно с помощью команды cd.

Выполните несколько раз команду python main.py. Вместо main.py должно быть имя вашего скрипта. Каждый раз вы увидите в консоли новое случайное число.

Аргументы - это обязательные параметры, которые должны идти в определённом порядке. Передаются по позиции, то есть имя аргумента при передаче не указывается.


Опции - это необязательные параметры, которые могут идти в любом порядке. Передаются по имени, выглядят как --option-name [value].


Общий вид: command [options] <arguments>. То есть сначала передаются все опции, потом все аргументы.


Аргументы и опции добавляются к команде с помощью декораторов @click.argument и @click.option.

Теперь использовать скрипт можно так:

Отдельный вид опций - это флаги. Для них не нужно указывать никакого конкретного значения, они либо есть, либо их нет. Во многих консольных приложениях используется флаг --verbose («многословный»). Давайте сделаем так, чтобы при наличии такого флага в консоль выводилось не только число, но и небольшое сообщение.

Теперь использовать скрипт можно так:

click автоматически добавляет команду --help, которая выводит в консоль справочную информацию по командам, зарегистрированным с помощью @click.command. Попробуйте выполнить ваш скрипт с флагом --help, и вы увидите следующее:

При регистрации опции можно передать аргумент help, в котором будет содержаться справочная информация об этой опции.

Для аргументов справочную информацию указывать нельзя.

Напишите CLI-приложение, которое будет принимать в качестве аргумента строку и подсчитывать количество уникальных букв в нём.

Например, для строки 'Мама мыла раму' приложение выведет следующий результат:

Если передан флаг --ignore-case (игнорировать регистр символов), то результат должен быть таким:

А если ещё и передан флаг --sort, то таким:

Обратная связь

Как и всегда, буду рад видеть ваши решения в комментариях. По каждому из них будет предоставлена обратная связь от наших код-ревьюеров.


Код нужно обязательно представить в 2 форматах:

- изображение, сгенерированное на https://carbon.now.sh/;

- ссылка на код, сгенерированная через codepile.net или аналогичный сервис.

Правила сообщества

Публиковать могут пользователи с любым рейтингом. Однако!


Приветствуется:

• уважение к читателям и авторам

• конструктивность комментариев

• простота и информативность повествования

• тег python2 или python3, если актуально

• код публиковать в виде цитаты, либо ссылкой на специализированный сайт


Не рекомендуется:

• допускать оскорбления и провокации

• распространять вредоносное ПО

• просить решить вашу полноценную задачу за вас

• нарушать правила Пикабу

3
Автор поста оценил этот комментарий

@SupportTech, добрый день! Можно ли как-то добавить отступы между изображениями? Сейчас они приклеиваются друг к другу, и приходится "лайфхачить", вставляя небольшие белые изображения-заглушки (см. пост). Но на тёмной теме это, естественно, работает не так, как хотелось бы.


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

показать ответы
0
Автор поста оценил этот комментарий

А есть ли для Python явная типизация в каком-то виде по аналогии с TypeScript? Потому что долго мозги себе долбал когда приходилось парсить выводы нейросетей с многомерными массивами, а по коду фиг поймёшь где какие размеры получаются после numpy преобразований.

раскрыть ветку (1)
1
DELETED
Автор поста оценил этот комментарий

Это была моя первая мысль ещё при написании первого варианта, но разве set(string) не сломает порядок букв в словаре?

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

А, ну в принципе логично, не подумал про это. Не всегда же флаг sort устанавливается. Тогда...

Иллюстрация к комментарию
1
DELETED
Автор поста оценил этот комментарий

Да, разница огромная.

В этом и есть моя проблема - мой код никто не читает!


Мой первый код работал правильно, но не был оптимизирован. Без вашей вычитки я бы так его и оставил, довольный тем, что он работает).

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Ну штош, рад, что помог :)

1
DELETED
Автор поста оценил этот комментарий

Был уверен, что "Counter" ломает порядок расположения знаков относительно введенной строки. Оказывается, что нет, и эту функцию можно использовать. Удалил лишнюю функцию, так как Counter её заменяет полностью, и объявлять дополнительную функцию чтобы включить Counter и потом вызывать её в принципе не имеет смысла.


Спасибо за разбор кода!


Попытка номер 3.

Код: https://www.codepile.net/pile/Mj2v6xjR

Иллюстрация к комментарию
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

В принципе, можно было и без Counter обойтись, там 1 несложный генератор словарей может подсчитать количество символов.

{letter: string.count(letter) for letter in unique_letters}


unique_letters формируем с помощью set(string).

показать ответы
1
DELETED
Автор поста оценил этот комментарий

Был уверен, что "Counter" ломает порядок расположения знаков относительно введенной строки. Оказывается, что нет, и эту функцию можно использовать. Удалил лишнюю функцию, так как Counter её заменяет полностью, и объявлять дополнительную функцию чтобы включить Counter и потом вызывать её в принципе не имеет смысла.


Спасибо за разбор кода!


Попытка номер 3.

Код: https://www.codepile.net/pile/Mj2v6xjR

Иллюстрация к комментарию
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Вот это уже прямо похоже на правду) Скорее всего, можно и ещё поковыряться, но уже не буду вас мучить. Почувствовали же разницу между исходным и финальным вариантом?

показать ответы
1
Автор поста оценил этот комментарий

Хочу порекомендовать typer. https://typer.tiangolo.com/

Он основан на click, но работает на аннотации типов. Он написан автором FastAPI, так что там поддерживаются те же идеи

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Офигенная вещь, спасибо! Под капотом rich, который рассматривал в одном из прошлых постов, то есть typer ещё и косметический ремонт для консоли проводит. На первый взгляд выглядит здорово, позже закопаюсь поглубже.

Иллюстрация к комментарию
0
DELETED
Автор поста оценил этот комментарий

Попытка номер 2.

Разбил на больше функций, сократил количество строк и "if".


Code: https://www.codepile.net/pile/rg2oOj84

Иллюстрация к комментарию
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Вектор движения верный, но есть ещё пара моментов.

1. Зачем функция, которая убирает из строки пробелы и приводит к нижнему регистру, таскает везде строку case? Получается перегруженная функция, которая вместо выполнения 1 чётко оформленной задачи пытается делать 2 разных дела одновременно.

В том месте, откуда эта функция вызывается, известно значение ignore_case. Просто используйте его тогда, когда будете формировать расширенный вывод для --verbose. То есть не стоит смешивать логику из блоков "вывод на экран" и "подготовка данных".

2. Попробуйте использовать множество и генератор словаря вместо хитровыделанной конструкции с генератором списков и not in chars.

показать ответы
2
DELETED
Автор поста оценил этот комментарий

Спасибо за супер полезный для меня пост.

Это то, что я хотел изучить, но руки не доходили.


Вроде работает =).

Ссылка: https://www.codepile.net/pile/l5eDqamr

Иллюстрация к комментарию
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Очень приятно видеть, что посты приносят пользу :)

Код действительно вроде бы работает) Но его можно сделать проще и короче. Попробуйте более чётко разделить всю логику на отдельные блоки:
1. Начальная подготовка данных;
2. Подсчёт букв;
3. Вывод на экран в нужном формате.

Сейчас эти блоки тесно переплетаются друг с другом, и это влечёт за собой обилие if/else и достаточно глубокую вложенность. Но задачу по формированию словаря вида {'М': 1, ...} можно решить в 5 строк кода, содержащих всего 1 if. Ну или 7 строк, если учитывать пустые строки, добавленные для красоты)

После того, как словарь будет сформирован, останется лишь вывести его на экран в нужном виде. Тут уже можно и сортировку учесть, и --verbose. Попробуйте)

показать ответы
0
Автор поста оценил этот комментарий
Урезонил.
раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Ну там реально двусмысленно в доке написано. Если бы я первый раз её увидел, то тоже, вероятно, недопонял бы этот момент.

1
Автор поста оценил этот комментарий

https://click.palletsprojects.com/en/8.1.x/why/#why-not-argp...
Вот тут и написано...

Click is internally based on optparse instead of argparse.
раскрыть ветку (1)
2
Автор поста оценил этот комментарий

Ну не знаю, выглядит может и проще чем argparse... Однако, они сами пишут, что Click основан на optparse, который в новых версиях python - deprecated. Стоит ли подтягивать его в новые проекты - хз.


С другой стороны, основан не значит, что использует. Да и кол-во звездочек намекает, что это мало кого заботит :)
раскрыть ветку (1)
1
Автор поста оценил этот комментарий
Это где такое написано? В официальной доке обратное говорится: у них своя реализация парсинга аргументов, не зависящая ни от optparse, ни от argparse.
показать ответы

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества