Что ожидается от скриптового языка:
- умеет работать с файлами (прочитать/записать csv/yaml/json)
- умеет работать с сетью (отправить http-запрос)
- сделать какую-то логику
- не требует установки окружения и компиляции
- все в одном файле
BASH удовлетворяет всему этому, но у него свои проблемы:
- Тяжело прочитать
- Тяжело отлаживать
- Тяжело писать нетривиальную логику
Казалось бы Python позволяет решить все эти проблемы, но очень быстро на Питоне хочется ставить какие-нибудь пакеты.
Как это сделать поудобнее?
- Ставим
poetry
. - Создаем папку для скрипта:
mkdir my-script-1
- Инициализируем там poetry:
cd my-script-1 && poetry init
. Или берем готовый файлpyproject.toml
:
[tool.poetry]
package-mode = false
[tool.poetry.dependencies]
python = "^3.12"
pandas = "^2.2.1"
requests = "^2.31.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
- Создаем файл для нашего скрипта:
touch main.py && chmod +x main.py
- Скачиваем зависимости:
poetry install --no-root
(или простоpoetry install
, если выставлено свойствоpackage-mode=false
) - Заходим в виртуальное окружение (от туда уже можно запускать скрипт):
poetry shell
, или напрямую запускаемpoetry run ./main.py
Сам скрипт (просто стартовый пример):
#!/usr/bin/env python3
# https://docs.python.org/3/library/dataclasses.html
from dataclasses import dataclass
# https://pandas.pydata.org/
import pandas as pd
# https://requests.readthedocs.io/en/latest/
import requests
from numpy import ndarray
@dataclass
class DocTags:
id: str
tags: [str]
def parse_line(values: ndarray) -> [DocTags]:
assert values.size == 2
doc_id: str = values[0]
tags_str: str = values[1]
tags = list(map(str.strip, tags_str.split(",")))
return DocTags(doc_id, tags)
def read_file(filename: str) -> [DocTags]:
data = pd.read_csv(filename, sep="\t", header=None)
return list(map(parse_line, data.values))
def main():
docs = read_file("example.tsv")
for doc in docs:
with open(f"out/{doc.id}.txt", 'w') as file:
file.write(str(doc) + "\n")
response = requests.put(f"https://httpbin.org/anything/{doc.id}", json=doc.tags)
file.write(f"status_code={response.status_code} json={response.text}\n")
if __name__ == '__main__':
main()
Естественно, это все открывается в PyCharm: там можно запускать и вставать в отладку.
В итоге, все-таки есть дополнительные шаги, но их не так уж и много.
PS. Домашнее задание: написать то же, но на чистом BASH. У меня получилось 22 строчки, но сложно читаемых и не поддерживаемых. Впрочем, для скриптов это не особо надо. Тем не менее, это заставило задуматься почему изначально все-таки выбрал BASH, а не Python.
PPS. Домашнее задание 2: переписать скрипт так, чтобы внешние библиотеки (pandas
и requests
не требовались – по идее это должно быть просто). Решение, если писать не хочется.
PPPS. Можно еще то же самое посмотреть на Kotlin (link1, link2), Ruby и других языках.