Топ-10 библиотек Python для Data Science
Уже довольно давно Python очаровывает ученых, занимающихся данными. Чем больше я взаимодействую с ресурсами, литературой, курсами, тренингами и людьми в науке о данных, тем более глубокие знания Python приобретаю. При этом, когда я только начинал развивать свои навыки Python, у меня был целый список библиотек, о которых мне предстояло узнать. И вот, через некоторое время.
Специалисты в области Data Science точно знают о библиотеках Python, которые можно использовать в науке о данных, но когда в интервью просят назвать их или указать их функцию, мы часто попадаем впросак или, возможно, не помним более 5 библиотек (это случилось со мной: / )
Сегодня я подготовил список из 10 библиотек Python, которые помогают в области Data Science, когда их использовать, каковы их особенности и преимущества.
В этой статье я кратко изложил 10 наиболее полезных библиотеках Python для data scientist’ов и инженеров, основываясь на моем недавнем опыте и исследованиях. Читайте статью до конца, чтобы узнать о 4 бонусных библиотеках!
1. Pandas
Pandas — это пакет Python с открытым исходным кодом, который предоставляет высокоэффективные, простые в использовании структуры данных и инструменты анализа для помеченных данных на языке программирования Python. Pandas расшифровывается как библиотека анализа данных Python. Кто-нибудь знал об этом?
Когда использовать? Pandas — это идеальный инструмент для обработки данных. Он предназначен для быстрой и простой обработки данных, чтения, агрегирования и визуализации.
Pandas берет данные в файле CSV или TSV или базу данных SQL и создает объект Python со строками и столбцами, который называется фреймом данных. Фрейм данных очень похож на таблицу в статистическом программном обеспечении, скажем, в Excel или SPSS.
Что можно делать с помощью Pandas?
1. Индексирование, манипулирование, переименование, сортировка, объединение фрейма данных;
2. Обновить, добавить, удалить столбцы из фрейма данных;
3. Восстановить недостающие файлы, обработать недостающие данные или NAN;
4. Построить гистограмму или прямоугольную диаграмму.
Это делает Pandas фундаментальной библиотекой в изучении Python для Data Science. Если вам интересно узнать 10 приемов Python Pandas, которые сделают вашу работу более эффективной, читайте нашу статью.
2. NumPy
NumPy — один из самых фундаментальных пакетов в Python — универсальный пакет для обработки массивов. Он предоставляет высокопроизводительные объекты многомерных массивов и инструменты для работы с массивами. NumPy — это эффективный контейнер универсальных многомерных данных.
Основной объект NumPy — это однородный многомерный массив. Это таблица элементов или чисел одного и того же типа данных, проиндексированная набором натуральных чисел. В NumPy размеры называются осями, а число осей называется рангом. Класс массива NumPy называется ndarray, он же array.
Когда использовать? NumPy используется для обработки массивов, в которых хранятся значения одного и того же типа данных. NumPy облегчает математические операции над массивами и их векторизацию. Это значительно повышает производительность и, соответственно, ускоряет время выполнения.
Что можно делать с помощью NumPy?
1. Основные операции с массивами: добавление, умножение, срез, выравнивание, изменение формы, индексирование массивов;
2. Расширенные операции с массивами: стековые массивы, разбиение на секции, широковещательные массивы;
3. Работа с DateTime или линейной алгеброй;
4. Основные нарезки и расширенное индексирование в NumPy Python.
О 4 приемах Python NumPy, которые должен знать каждый новичок, читайте здесь.
3. SciPy
Библиотека SciPy является одним из ключевых пакетов, которые составляют стек SciPy. Теперь есть разница между SciPy Stack и библиотекой SciPy. SciPy основывается на объекте массива NumPy и является частью стека, который включает в себя такие инструменты, как Matplotlib, Pandas и SymPy с дополнительными инструментами.
Библиотека SciPy содержит модули для эффективных математических процедур, таких как линейная алгебра, интерполяция, оптимизация, интеграция и статистика. Основной функционал библиотеки SciPy построен на NumPy и его массивах.
Когда использовать? SciPy использует массивы в качестве базовой структуры данных. Он имеет различные модули для выполнения общих задач научного программирования, таких как линейная алгебра, интеграция, матанализ, обыкновенные дифференциальные уравнения и обработка сигналов.
Что можно делать с помощью SciPy?
1. Математические, научные, инженерные вычисления;
2. Процедуры численной интеграции и оптимизации;
3. Поиск минимумов и максимумов функций;
4. Вычисление интегралов функции;
5. Поддержка специальных функций;
6. Работа с генетическими алгоритмами;
7. Решение обыкновенных дифференциальных уравнений.
4. Matplotlib
Это, несомненно, моя любимая и основная библиотека Python. Вы можете создавать истории с данными, визуализированными с помощью Matplotlib. Еще одна библиотека из стека SciPy — Matplotlib — строит 2D-фигуры.
Когда использовать? Matplotlib — это библиотека Python, предоставляющая API для встраивания графиков в приложения. Очень напоминает MATLAB, встроенный в язык программирования Python.
Что можно делать с помощью Matplotlib?
Гистограммы, столбцовые диаграммы, точечные диаграммы, круговые диаграммы — Matplotlib может отображать широкий спектр визуализаций. Приложив немного усилий, с Matplotlib, вы можете создавать любые визуализации:
1. Линейные диаграммы;
2. Точечные диаграммы;
3. Диаграммы с областями;
4. Столбцовые диаграммы и гистограммы;
5. Круговые диаграммы;
6. Диаграммы «стебель-листья»;
7. Контурные графики;
8. Поля векторов;
Matplotlib также облегчает использование меток, сеток, легенд и некоторых других объектов форматирования. В общем, речь идет обо всем, что можно нарисовать!
5. Seaborn
Итак, когда вы читаете официальную документацию по Seaborn, она определяется как библиотека визуализации данных на основе Matplotlib, предоставляющем высокоуровневый интерфейс для изображения интересных и информативных статистических графиков. Проще говоря, seaborn — это расширение Matplotlib с дополнительными возможностями.
Так в чем разница между Matplotlib и Seaborn? Matplotlib используется для основного построения столбцовых, круговых, линейных, точечных диаграмм и пр., в то время как Seaborn предоставляет множество шаблонов визуализации с меньшим количеством синтаксических правил, причем более простых.
Что можно делать с помощью Seaborn?
1. Определять отношения между несколькими переменными (корреляция);
2. Соблюдать качественные переменные для агрегированных статистических данных;
3. Анализировать одномерные или двумерные распределения и сравнивать их между различными подмножествами данных;
4. Построить модели линейной регрессии для зависимых переменных;
5. Обеспечить многоуровневые абстракции, многосюжетные сетки.
Seaborn — это отличный вариант для библиотек визуализации R, таких как corrplot и ggplot.
6. Scikit Learn
Scikit Learn, представленный миру как проект Google Summer of Code, представляет собой надежную библиотеку машинного обучения для Python. Он включает в себя алгоритмы ML, такие как SVM, random forests, k-means кластеризацию, спектральную кластеризацию, сдвиг среднего значения, перекрестную проверку и многие другие. Даже NumPy, SciPy и связанные с ними научные операции поддерживаются Scikit Learn, при этом Scikit Learn является частью SciPy Stack.
Когда использовать? Scikit-learn предоставляет ряд контролируемых и неконтролируемых алгоритмов обучения через согласованный интерфейс в Python. Scikit learn будет вашим руководством для того, чтобы модели контролируемого обучения, такие как Naive Bayes, группировали непомеченные данные, такие как KMeans.
Что можно делать с помощью Scikit Learn?
1. Классификация: обнаружение спама, распознавание изображений;
2. Кластеризация: воздействия лекарственных препаратов, цена акций;
3. Регрессия: сегментация клиентов, группировка результатов эксперимента;
4. Уменьшение размерности: визуализация, повышенная эффективность;
5. Выбор модели: повышенная точность благодаря настройке параметров;
6. Предварительная обработка: подготовка входных данных в виде текста для обработки с помощью алгоритмов машинного обучения.
Scikit Learn фокусируется на моделировании данных; не манипулировании данными. Для обобщения и манипуляции у нас есть NumPy и Pandas.
7. TensorFlow
Еще в 2017 году я получил TensorFlow USB в знак признательности за то, что был потрясающим докладчиком на мероприятии Google WTM, хаха. На USB был загружен официальный документ о TensorFlow. Не имея представления о том, что такое TensorFlow, я его погуглил.
TensorFlow — это библиотека AI, которая помогает разработчикам создавать крупномасштабные нейронные сети со многими слоями, используя графики потоков данных. TensorFlow также облегчает построение моделей глубокого обучения, продвигает современную технологию ML / AI и позволяет легко развертывать приложения на базе ML.
Одним из наиболее развитых веб-сайтов среди всех библиотек является TensorFlow. Гиганты, такие как Google, Coca-Cola, Airbnb, Twitter, Intel, DeepMind, все используют TensorFlow!
Когда использовать? TensorFlow достаточно эффективен, когда дело доходит до классификации, восприятия, понимания, обнаружения, прогнозирования и создания данных.
Что можно делать с помощью TensorFlow?
1. Распознавание голоса / звука — IoT, автомобильная промышленность, безопасность, UX/UI, телекоммуникации;
2. Анализ настроений — в основном для CRM или CX;
3. Текстовые Приложения — Обнаружение угроз, Google Translate, Gmail Smart Reply;
4. Распознавание лиц — Facebook’s Deep Face, Photo tagging, Smart Unlock;
5. Временной ряд — рекомендации от Amazon, Google и Netflix;
6. Обнаружение видео — обнаружение движения, обнаружение угроз в реальном времени в играх, безопасности, аэропортах.
Более подробно о TensorFlow рассказываем в статье.
8. Keras
Keras — это высокоуровневый API TensorFlow для создания и обучения кода глубоких нейронных сетей. Это библиотека нейронных сетей с открытым исходным кодом на Python. С Keras статистическое моделирование, работа с изображениями и текстом намного легче с упрощенным кодированием для глубокого обучения.
В чем разница между Keras и TensorFlow?
Keras — это нейросетевая библиотека, написанная на языке Python, а TensorFlow — это библиотека с открытым исходным кодом для различных задач машинного обучения. TensorFlow предоставляет как высокоуровневые, так и низкоуровневые API, в то время как Keras предоставляет только высокоуровневые API. Keras создан для Python и делает его более удобным, модульным и компонуемым, чем TensorFlow.
Что можно делать с помощью Keras?
1. Определить процентную точность;
2. Функция вычисления потерь;
3. Создать пользовательские функциональные слои;
4. Встроенные функции обработки данных и изображений;
5. Функции с повторяющимися блоками кода: глубиной 20, 50, 100 слоев.
9. Statsmodels
Когда я сначала изучил R, проведение статистических тестов и исследование статистических данных казались мне самым простым в R и я избегал Python для статистического анализа до тех пор, пока я не изучил Statsmodels в Python.
Когда использовать? Statsmodels — это универсальный пакет Python, который обеспечивает простые вычисления для описательной статистики и оценки и формирования статистических моделей.
Что можно делать с помощью Statsmodels?
1. Линейная регрессия;
3. Метод наименьшего квадрата (OLS);
4. Анализ выживания;
5. Обобщенные линейные модели и байесовская модель;
6. Однофакторный и двухфакторный анализ, проверка гипотез (в основном, что может сделать R!).
10. Plotly
Plotly — это типичная графическая библиотека для Python. Пользователи могут импортировать, копировать, вставлять или передавать данные, которые должны быть проанализированы и визуализированы. Plotly предлагает изолированную версию Python (где вы можете запустить Python, ограниченный в своих возможностях). Теперь осталось понять, что значит ограниченная версия, но я точно знаю, что Plotly облегчает задачу!
Когда использовать? Вы можете использовать Plotly, если хотите создавать и отображать фигуры, обновлять фигуры, наводить курсор на текст для получения подробной информации. Plotly также имеет дополнительную функцию отправки данных на облачные серверы. Это интересно!
Что можно делать с помощью Plotly?
Библиотека графиков Plotly имеет широкий спектр графиков, которые вы можете построить:
1. Основные диаграммы: линейные, круговые, точечные, пузырьковые, Ганта, санбёрст, древовидные, санкей, графики с областями;
2. Статистические стили и стили Seaborn: ошибки, гистограммы, диаграммы Facet и Trellis, деревообразные графики, графики-скрипки, линии тренда;
3. Научные карты: контур, троичный сюжет, логарифмический график, поля векторов, ковровый график (Carpet plot), радарчарт, тепловые карты Роза ветров и Полярный сюжет;
4. Финансовые графики;
8. Взаимодействие Jupyter Widgets.
Plotly это типичная библиотека графиков. Подумайте о визуализации и Plotly сделает это!
Итак, мы изучили путеводитель по топ 10 библиотекам Python для науки о данных, а теперь рассмотрим наши 4 бонусные библиотеки!
1. SpaСy
SpaCy — это библиотека с открытым исходным кодом, используемая для продвинутого NLP для Python и Cython (язык программирования Python, обеспечивающий ощущение и производительность в стиле C с кодом Python, а также синтаксис на основе C).
2. Bokeh
Bokeh — это библиотека Python, которую я бы назвал интерактивной визуализацией данных. С такими инструментами, как Tableau, QlikView или PowerBI, зачем нам Bokeh? Во-первых, Bokeh позволяет очень быстро строить сложные статистические графики с помощью простых команд. Он поддерживает вывод HTML, блокнота или сервера. Во-вторых, можно интегрировать визуализацию Bokeh в приложения Flask и Django или визуализации, написанные в других библиотеках, таких как matplotlib, seaborn, ggplot.
3. Gensim
Gensim — это то, что, по моему мнению, сильно отличается от того, что мы встречали до этого. Он автоматически извлекает семантические темы из документов без труда и с высокой эффективностью. Алгоритмы Gensim не контролируются, это означает, что никакой человеческий ввод не требуется — просто текстовые документы, и затем выполняется извлечение.
4. NLTK
NLTK (Natural Language Toolkit) в основном работает с человеческим языком, а не с компьютерным, чтобы применять обработку естественного языка (NLP). Он содержит библиотеки обработки текста, с помощью которых вы можете выполнять токенизацию, парсинг, классификацию, выделение, тегирование и семантическое обоснование данных. На основе функционала этой библиотеки может показаться, что она повторяется, но каждая библиотека в Python была написана для повышения некоторой эффективности.
Популярные инструменты Python: библиотеки и фреймворки

Python — один из самых популярных языков программирования в наши дни. Он одинаково эффективно используется для создания разнообразных приложений как новичками, так и опытным специалистами.
Одной из главных причин такой популярности, наряду с легким синтаксисом и универсальностью, стало наличие огромного числа дополнительных инструментов, значительно упрощающее и ускоряющее программирование на Python. В предыдущей статье мы уже рассказывали о наиболее популярных средах программирования и редакторах Python. В этом обзоре постараемся перечислить лучшие фреймворки и библиотеки Python, которые сегодня должен использовать каждый разработчик.
Что такое фреймворки Python
Это наборы модулей или пакетов, помогающих разработчикам писать веб-приложения на языке Python. Главной задачей этих инструментов разработки является упрощение рутинных процессов программирования и поддержка сложных с технической точки зрения проектов.
Самое большое достоинство фреймворков — комплексность. Они содержат в себе все необходимое для создания полноценной программы. Благодаря им, можно забыть о поиске отдельных библиотек для решения мелких задач и о проблемах совместимости.
Фреймворки Python имеют более высокий уровень абстракции исходного кода и потенциал метапрограммирования для разработки больших и сложных систем, а также множество библиотек для уникальных функций.
Для каких задач предназначены фреймворки Python
- Маршрутизация URL-адресов и манипулирование данными.
- Форматирование вывода с использованием механизмов шаблонизации.
- Конфигурация подключения к базе данных.
- Защита данных от подделки межсайтовых запросов (CSRF).
- Хранение и извлечение сеансов.
Лучшие фреймворки Python
Qt / PyQt

Qt — кроссплатформенная среда разработки и графический фреймворк для программистов С++, однако она имеет привязки для других языков программирования, включая Python, Ruby и Java. Привязка для Python называется PyQt и представляет собой комплект инструментов для работы с графическим интерфейсом.
Особенности PyQt
- Позволяет работать с базами данных SQL.
- Графический фреймворк Python оснащен полнофункциональным браузером.
- Содержит в себе конструктор графического интерфейса пользователя.
- Дает добавлять новые элементы управления GUI.
Для чего подходит: для создания десктопных приложений с графическим интерфейсом пользователя.
Flask

Микрофреймворк и библиотека Flask распространяется бесплатно по BSD лицензии. Для корректной работы с Python Flask необходим шаблон Jinja2 и инструментарий от Werkzeug WSGI. Имеет модульный дизайн, что делает его легко адаптируемым.
Особенности Flask
- Совместим с Google App Engine.
- Имеет встроенный сервер разработки и отладчик.
- Оснащен встроенной поддержкой модульного тестирования.
- Flask — фреймворк с акцентом на простоту работы.
Для чего подходит: для разработки и ведения маленьких и несложных проектов. Несмотря на это, используется такими крупными компаниями, как LinkedIn и Pinterest.
Tornado

Фреймворк Python и асинхронная сетевая библиотека в одном приложении. Изначально Tornado разрабатывался для компании FriendFeed, которая была приобретена Facebook в 2009 году.
Tornado использует не блокирующую сеть и выдерживает большие нагрузки — до десяти тысяч одновременных подключений. Асинхронный фреймворк Python поддерживает перевод и локализацию, имеет web-шаблоны, а также позволяет реализовывать сторонние способы авторизации и аутентификации.
Особенности Tornado
- Бесплатный фреймворк с открытым исходным кодом.
- Позволяет реализовывать сторонние методы авторизации.
- Поддерживает аутентификации пользователей.
- Имеет асинхронную библиотеку для сетей.
Для чего подходит: для разработки приложений, от которых требуется высокая производительность.
Django

Django — фреймворк Python с большим количеством функций, что позволяет быстрее писать код и разрабатывать сложные веб-приложения. Django следует принципу DRY (Don’t Repeat Yourself), который заключается в том, что код должен оставаться простым и неповторяющимся.
Фреймворк использует технологию ORM (Object-relational mapper), что позволяет сопоставлять объекты с таблицами баз данных, а также упрощает процесс переноса данных из одной базы в другую.
Особенности Django
- Бесплатный фреймворк с открытым исходным кодом.
- Имеет мощный движок шаблонов.
- Обработчики URL конфигурируются при помощи регулярных выражений.
- Оснащен простой, но мощной системой URL-адресов.
- Встроенная поддержка MySQL, PostgreSQL, SQLite и Oracle Database. Фреймворк может поддерживать и другие базы данных, однако, для этого необходима установка дополнительных пакетов.
- Оснащен системой кеширования.
- Включает в себя встроенную аутентификационную систему.
- С помощью установки библиотеки Wagtail Django может получить функционал полноценной CMS.
Для чего подходит: для более продуктивной работы с новостными и контентными веб-ресурсами.
FastAPI

FastAPI — это современный и высокопроизводительный веб-фреймворк для создания API на основе Python версии 3.6 и выше. В FastAPI реализован ряд продвинутых языковых функций (включая стандартные подсказки типов Python) в сочетании с существующими стандартами OpenAPI.
Эта библиотека API сегодня используется многими технологическими гигантами, вроде Uber, Netflix и Microsoft для разработки своих приложений.
Особенности FastAPI
- Очень высокая производительность, сравнимая с Node.js и Go, благодаря таким библиотекам, как Starlette (для веб) и Pydantic (для обработки и валидации данных).
- Интуитивно понятный интерфейс и удобный редактор.
- Простота в использовании и обучении.
- Возможность использования нескольких функций из каждого объявления параметра, сводящая к минимуму дублирование кода.
- Автоматическая интерактивная документация по API, включая 2 пользовательских интерфейса: Swagger UI и ReDoc.
- Полностью совместим с открытыми стандартами для API: OpenAPI (экс Swagger) и JSON.
- Поддерживает асинхронный код с использованием ключевых слов async/await для Python.
Для чего подходит: для быстрого и качественного создания служб API на Python.
PyTorch / PyTorch Lightning

Крупнейший фреймворк для машинного обучения, который позволяет разработчикам выполнять тензорные вычисления с ускорением графического процессора, создавать динамические вычислительные графики и автоматически рассчитывать градиенты. Помимо этого, PyTorch предлагает богатые API для решения проблем приложений, связанных с нейронными сетями.
PyTorch разработан исследовательской группой искусственного интеллекта Facebook на основе Torch — библиотеки machine learning с открытым исходным кодом, реализованной на C с оболочкой на Lua. На базе фреймворка создана целая экосистема дочерних продуктов, включающая фреймворк PyTorch Lightning, упрощающий процесс обучения искусственного интеллекта.
Особенности PyTorch
- Гибридный интерфейс обеспечивает простоту использования и гибкость в активном режиме, а также плавный переход в графический режим для повышения скорости, оптимизации и функциональности в средах выполнения C++.
- Встроенная поддержка асинхронного выполнения коллективных операций и одноранговой связи, доступная в Python и C++.
- PyTorch создан для глубокой интеграции с Python, поэтому его можно использовать с популярными библиотеками и пакетами этого языка, такими как Cython и Numba.
- Активное сообщество исследователей и разработчиков, создавшее богатую экосистему инструментов и библиотек и поддерживающее разработки в различных областях, от компьютерного зрения до обучения с подкреплением.
Для чего подходит: для задач машинного обучения (ML) и глубокого обучения (DL), таких, как обработка естественного языка.
Pyramid

Фулстек-фреймворк с открытым исходным кодом предлагает широкие возможности для разработки как простых, так и сложных веб-приложений на Python. За счёт своей главной особенности — лёгкой расширяемости Pyramid обеспечивает прочный баланс между легкостью и многофункциональностью.
Особенности Pyramid
- Гибкая аутентификация и авторизация.
- Расширяемость конфигурации.
- Декораторы функций.
- Предикаты.
- Рендереры.
- Однофайловые приложения.
- Спецификации шаблонов и активов.
- Генерация URL.
Для чего подходит: для достижения максимально возможных результатов в веб-разработке при использовании минимальных ресурсов.
Falcon

Один из наиболее популярных фреймворков Python для быстрого создания веб-API, таких, как конфигурационные файлы. Falcon основан на архитектуре HTTP и REST. Этот ASGI/WSGI веб-фреймворк используют в своих технологических стеках такие крупные игроки сферы IT, как LinkedIn, OpenStack и RackSpace.
Особенности Falcon
- 100% покрытие кода.
- Повышенный уровень безопасность — точный и эффективный механизм борьбы с ошибками HTTP и уязвимостям.
- Модульное тестирование с помощью помощников и моков WSGI.
- Предварительная обработка исключений.
- Классы ресурсов на основе REST.
- Поддержка Cython, дающая прирост скорости.
- Компоненты промежуточного программного обеспечения и хуки.
- Обработка запроса DRY.
- Шаблоны URL, предлагающие интуитивно понятную маршрутизацию.
Для чего подходит: для создания высокопроизводительных крупномасштабных серверных приложений, API и микросервисов
Dash

Dash — микрофреймворк Python с открытым исходным кодом, предназначенный исключительно для разработки аналитических приложений для machine learning и data science. Он создан на базе связки Plotly.js, Flask, React.Js, HTML и CSS.
Dash позволяет разработчикам одним щелчком мыши создавать все, что необходимо для надежного управления базами данных и кроссплатформенными мобильными веб-приложениями. Этот популярный фреймворк Python состоит из двух частей: визуальной, описывающий внешний вид приложения и функциональной, описывающей его взаимодействие с пользователями.
Особенности Dash
- Высокий уровень настройки и встроенная обработка ошибок, а также интеграция с LDAP и маршрутизация URL-адресов через сервер развертывания.
- Удобный и простой интерфейс, объединяющий такие элементы управления UI, как графики, раскрывающиеся списки, ползунки и т. д.
- Поддержка многопользовательского режима.
- Низкая потребность в шаблонном коде.
- Интеграция с AD/LDAP.
- Настраиваемость.
- Поддержка плагинов.
Для чего подходит: для создания дэшбордов в машинном обучении и науке о данных.
CherryPy

Как и Django, CherryPy также относится к средам веб-разработки с открытым исходным кодом, которые в основном используют объектно-ориентированную парадигму. Он поставляется с собственными многострочными веб-серверами Python WSGI, совместимыми с HTTP/1.1.
Это один из лучших фреймворков Python для быстрой разработки. Он предлагает модули контроллера и фреймворк установки, которые упрощают доступ к данным, загрузку файлов, механизмы шаблонов и обработку сеансов.
Особенности CherryPy
- Веб-сервер с пулом потоков WSGI, совместимый с HTTP/1.1.
- Многопоточность — возможность одновременно запускать несколько веб-серверов HTTP.
- Кроссплатформенность — возможность запускать приложения во всех операционных системах с поддержкой Python, включая Windows, Mac и Linux или Unix.
- Android-совместимость.
- Мощная система установки и настройки.
- Гибкая система плагинов.
- Встроенная поддержку задач документирования, таких как охват, тестирование и профилирование.
Для чего подходит: для быстрой разработки веб-приложений на Python и сокращению трудозатрат на написание кода и формирование надежной базы данных.
Что такое библиотеки Python
Библиотеки Python — это коллекции дополнительных модульных компонентов кода для «змеиного языка», заточенных под определенные тонкие задачи. Для управления ими необходимы специальные навыки, овладев которыми можно сделать программирование на «Питоне» значительно более эффективным.
Модули сторонних библиотек нужны для расширения функционала стандартной библиотеки Python. С помощью набора функций, который они предлагают, можно поэтапно работать над большими проектами и решать различные комплексные задачи.
Основное отличие фреймворка от библиотеки в том, что первый является готовым, самодостаточным «механизмом», для запуска которого необходимо добавить логическую структуру программы. Библиотеки же представляют собой отдельные модули, которые разработчик подключает к собственному коду, тем самым внедряя в свой проект новые возможности.
Лучшие библиотеки Python
Tensor Flow

Широко распространенная библиотека с открытым исходным кодом. Изначально Tensor Flow предназначен для работы с Python, однако она предоставляет доступ и к базовому API C++.
В отличие от других МО/ГО библиотек, Tensor Flow разрабатывался для использования не только в исследованиях и разработках, но для производственных целей. Среди наиболее ярких проектов, созданных с его помощью, можно выделить самообучающийся алгоритм Google Rank Brain и нейронную сеть DeepDream.
Особенности Tensor Flow
- Программный продукт для свободного использования под лицензией Apache 2.0.
- Библиотека TensorFlow позволяет оптимизировать модели для их развертывания в условиях ограниченных ресурсов. Это возможно при использовании специального фреймворка — TensorFlow Serving.
- Большое сообщество. Библиотека создавалась корпорацией Google, которая и по сей день работает над улучшением стабильности.
Для чего подходит: для реализации мощных алгоритмов, рассчитанных на решение распространенных задач машинного обучения. Например, Tensor Flow может создавать нейронные сети для классификации рукописных цифр, распознавания изображений и встраивания слов.
NumPy

NumPy – одна из лучших библиотек машинного обучения в Python. Она предоставляет пользователям объект многомерного массива с высокой производительностью и инструменты для работы с такими массивами.
Особенности NumPy
- Библиотека интерактивна и имеет понятный интерфейс.
- Поддерживает многомерные массивы и высокоуровневые математические функции.
- NumPy совместим с другими популярными библиотеками.
- Математическая библиотека предлагает комплексные математические функции, включая генератор рандомных чисел. Может работать с процессами линейной алгебры.
- Сообщество NumPy, совместно со SkyPy, поддерживает большое онлайн-руководство, благодаря которому можно легко научиться пользоваться библиотекой.
Для чего подходит: помимо стандартных научных задач, NumPy часто применяется для создания многомерных массивов обобщенных данных.
Keras

Keras – открытая библиотека и API для глубокого изучения, написанный на Python и работающий поверх платформы машинного обучения TensorFlow. Основная задача Keras — упрощение экспериментирования с глубокими нейронными сетями за счет высокоуровневой абстракции. Главные ориентиры библиотеки для deep learning — удобство использования, модульность и расширяемость.
Особенности Keras
- Работает одинаково хорошо и на GPU, и на CPU.
- Поддерживает практически все разновидности нейронных сетей, включая сверточные и рекуррентные.
- Каждая модель в фреймворке портативна и может быстро расширяться сторонними модулями.
- Библиотека Keras создает специальный вычислительный граф, используя внутреннюю инфраструктуру, что может негативно сказываться на скорости работы.
Для чего подходит: для превращения модели проекта в реальный продукт.
Pandas

Pandas – библиотека машинного обучения на Python, предоставляющая структуры данных высокого уровня и большой набор инструментов для анализа данных.
Pandas содержит в себе методы для группировки, объединения данных и их фильтрации. Библиотека модулей Python оснащена множеством полезных функций, включая повторное индексирование, сортировку, агрегирование, конкатенацию и визуализацию.
Особенности Pandas
- Библиотека Pandas Python способна представлять данные тем способом, который лучше подходит для анализа при помощи структур Series и DataFrame.
- Инструмент предлагает несколько методов для удобной фильтрации данных.
- Библиотека способна читать данные из разных форматов, включая CSV, TSV и MS Excel.
- Дает пользователям самостоятельно выставить настройки интерпретатора.
- Позволяет создавать песочные структуры данных с помощью модуля тестирования.
- «Панды» отлично взаимодействует с библиотеками искусственного интеллекта, а многие из них включают объекты Pandas в списки источников для обучения.
Для чего подходит: для работы над задачами в сфере машинного обучения и анализа больших данных.
Pillow

Любой топ библиотек Python будет неполным без Pillow — основной библиотеки изображений для быстрого доступа к данным, хранящимся в разных пиксельных формах. Библиотека поддерживает большой список форматов файлов и дает широкие возможности для обработки изображений разного качества.
Особенности Pillow
- Много возможностей по обработке изображений, включая изменение размера, обрезку, инверсию, геометрическое преобразование.
- Позволяет управлять пикселями.
- Позволяет создавать новые декодеры файлов для расширения списка поддерживаемых форматов.
- Имеет функцию фильтрации изображений. Например: размытие, контурирование или сглаживание.
Для чего подходит: для работы с растровой графикой.
Requests

Requests — HTTP-библиотека Python, выпущенная под лицензией Apache License 2.0. Главная задача библиотеки «Запросов» — упрощение отправки запросов, по сравнению со встроенными библиотеками urllib/urllib2.
Особенности Requests
- Поддерживает Python 2.7 и Python 3.5+. Отлично работает на PyPy.
- Библиотека Requests Python использует для отправки запросов urllib3 и httplib.
- Дает несложный API для запросов HEAD, POST, PUT, PATCH и DELETE.
- Автоматически добавляет строки запросов к адресам и кодирует данные POST.
Для чего подходит: для работы с HTTP запросами в Python.
Numba

Эта библиотека Python дает пользователю возможность компилировать код сразу после его выполнения (компилятор «точно-в-срок» / Just-in-Time). Таким образом достигается скорость кода C, не отказываясь от простоты Python.
Numba может определять тип процессора, на котором работает, и использовать аппаратно зависимую оптимизацию и различные потоки (чего Python сам по себе сделать не может). Компилятор создан для работы с Numpy — одной из самых известных библиотек для машинного обучения Python.
Особенности Numba
- Предназначен для кода, использующего массивы NumPy, функции и циклы.
- С помощью высокопроизводительных функций, написанных на «Питоне», может сделать код Python быстрее до 100 раз (до уровня C/C++ и FORTRAN).
- Поставляется с дистрибутивом Anaconda.
- Плохо совместим с динамическими структурами данных.
- Требует поддержки JIT-компилятора от системы.
Для чего подходит: для улучшения производительности компилируемых языков, включая Python.
SciPy

SciPy (Scientific Python) — бесплатная библиотека Python с открытым исходным кодом для машинного обучения, которая широко используется для высокоуровневых вычислений.
Библиотека SciPy содержит модули для оптимизации, линейной алгебры, интерполяции, интеграции и статистики. Библиотека для data science венчает собой целую экосистему или стек научно-инженерных библиотек, куда помимо SciPy, входят такие популярные инструменты, как NumPy, Matplotlib, Pandas, IPython и SymPy.
Особенности SciPy
- Библиотека разработана на основе расширения Python NumPy.
- Активное сообщество.
- Команды высокого уровня для работы с данными и их визуализации.
- Обработка многомерных изображений с помощью подмодуля SciPy ndimage.
- Включает встроенные функции для решения дифференциальных уравнений.
- Все функции в подмодулях SciPy хорошо документированы.
Для чего подходит: для упрощения интенсивных вычислений в таких областях, как математика, наука о данных, машинное обучение и инженерия.
Pygame

Самая старая и популярная модульная библиотека Python для игр. Pygame построен на базе C, Python, Open GL и библиотеки SDL (Simple DirectMedia Layer). Последний обеспечивает низкоуровневый доступ к компьютерному и графическому оборудованию через OpenGL и Direct3D.
Эта кроссплатформенная библиотека Python предлагает разработчикам игр множество удобных функций, включая полностью модульный, простой в использовании и оптимизированный код, поддержку многоядерных систем и переносимость, многоканальный звук, обнаружение столкновений, преобразования, поддержку рисования, векторную математику, манипулирование пиксельным массивом и многое другое.
Особенности Pygame
- Библиотека Pygame Python проста и портативна.
- Структура использует оптимизированную сборку и код для основных функций.
- Возможность использовать многоядерные процессоры при создании игр на Python.
- Минимализм в использовании кода.
Для чего подходит: для разработки игр на языке Python.
Scikit-learn

Это библиотека на языках Python, C, C++ и Cython, считается одним из лучших инструментов для машинного обучения и работы со сложными данными. Scikit-learn в основном ориентирован на различные концепции моделирования данных, такие как регрессия, классификация, кластеризация, выбор модели и т. д.
Библиотека Scikit-learn с открытым исходным кодом написана на основе Numpy, Scipy и Matplotlib. Она легко интегрируется с другими библиотеками ML, такими как Numpy и Pandas (анализ) и Plotly (отображение данных) для целей визуализации. Эта библиотека крайне полезна как в контролируемом, так и в неконтролируемом машинном обучении.
На основе Scikit-learn создана еще одна крайне популярная библиотека Python — коллекция алгоритмов обработки изображений Scikit-image. Она часто используется для работы с массивами NumPy в качестве объектов изображений.
Особенности Scikit-learn
- Предварительная обработка данных для алгоритмов машинного обучения.
- Кроссвалидация на основе более чем одной метрики. В библиотеке существуют различные методы перекрестной проверки точности контролируемых моделей на невидимых данных.
- Несколько наборов данных для изучения и тестирования моделей.
- Поддержка различных методов регрессии и классификации.
- Кластерный анализ.
- Для полноценного использования нужно также установить NumPy и SciPy.
Для чего подходит: для предиктивного анализа данных в машинном обучении.
Другие популярные библиотеки Python

Учитывая тот факт, что сегодня общее количество библиотек в Python приближается к 140 000, рассказать о каждой из них в рамках одного обзора просто не получится. Однако, для полноты картины, ниже мы приведем обширный список полезных и актуальных библиотек «Питона», которые охватывают широкий круг практических задач в сфере машинного обучения, науки о данных и искусственного интеллекта.
- Matplotlib — базовая пайтоновская библиотека для построения графиков, а также основа для других расширенных библиотек построения графиков на Python. Библиотека для работы с графиками имеет два разных API — Pyplot и объектно-ориентированный интерфейс.
- Kivy — библиотека применяется для разработки мобильных приложений и программного обеспечения мультисенсорных приложений с NUI (естественным пользовательским интерфейсом). Бесплатная библиотека языка Python с открытым исходным кодом распространяется по лицензии MIT и работает на Android, iOS, Linux, macOS и Windows.
- Bokeh — библиотека для визуализации данных упрощает создание информационных панелей, приложений данных и интерактивных графиков.
- Tkinter — стандартная GUI библиотека Python для создания графического интерфейса приложений. Библиотека Tkinter предлагает мощный объектно-ориентированный интерфейс для инструментария Tk GUI.
- Seaborn — библиотека визуализации данных для Python, построенная на основе Matplotlib и тесно интегрированная со структурами данных Pandas.
- Evidently — инструмент, который помогает оценивать модели машинного обучения во время проверки и отслеживать их в рабочей среде.
- SymPy — легкая библиотека Python для числовой и символьной математики.
- CuPy — библиотека для реализации многомерного массива на CUDA, совместимого с NumPy.
- Jina— платформа нейронного поиска, которая позволяет любому создавать масштабируемые поисковые приложения для глубокого обучения за считанные минуты. Часто используется совместно с библиотекой Finetuner, которая предоставляет удобный веб-интерфейс для улучшения представления результатов поиска.
- docker-py — библиотека Python дляAPI Docker Engine.
- AIOHTTP — асинхронный HTTP-клиент / сервер для asyncio и Python.
- IPython — библиотека имеет архитектуру, облегчающую параллельные и распределенные вычисления.
- AugLy— библиотека для увеличения данных (Data Augmentation) от Meta Research, которая поддерживает более 100 типов дополнений к аудио, изображениям, тексту и видео.
- TelegramBotAPI (aka telebot) — самая популярная Python библиотека для ботов Telegram.
- Telethon — клиентская asyncio библиотека Python для работы с Telegram API по протоколу MTProto в качестве пользователя или через учетную запись бота (как альтернатива API бота).
- vk / vk_api — библиотеки для работы с VK API в Python.
- Django Ninja — быстрый веб-фреймворк на основе OpenAPI и JSON для создания API с Django.
- Awkward Array — библиотека для работы со структурами в формате JSON, с помощью идиом NumPy.
- Twisted — сетевой движок, управляемый событиями, написан на Python и распространяется под лицензией MIT с открытым исходным кодом.
- SQLModel— библиотека для настройки взаимодействия баз данных SQL с объектами Python.
- Textual — библиотека и фреймворк TUI (Text User Interface) для Python, вдохновленный современной веб-разработкой, от автора популярной текстовой библиотеки Rich.
- natasha — основная библиотека одноименного проекта natasha (НТШ), предоставляющего инструментарий для машинной обработки естественного русского языка (NLP).
- opyrator — библиотека превращает функции Python в готовые к работе микросервисы на базе FastAPI, Streamlit и Pydantic.
- MoviePy — компактная, но практичная библиотека Python для редактирования видео.
- VISSL — библиотека для самоконтролируемого обучения на изображениях от Meta Research.
- YOLOX — версия алгоритма YOLO без привязки. Быстрая и точная модель обнаружения объектов на изображении с более простым дизайном, но более высокой производительностью.
- layout-parser — анализ изображений документов на основе глубокого обучения: обнаружение абзацев, заголовков, изображений и многого другого на страницах со сложными макетами.
- SAHI — умная библиотека для обнаружения объектов на больших изображениях без ущерба для производительности с помощью нарезки. Поддерживает как ограничивающие рамки, так и маски по категориям.
- lightseq — высокопроизводительная библиотека обучения и логического вывода для обработки и генерации последовательностей, реализованная в CUDA. NLP библиотека позволяет эффективно вычислять современные модели NLP (BERT, GPT, Transformer и др.).
- Top2Vec — библиотека для тематического моделирования автоматически определяет темы, присутствующие в тексте, и создает объединенные встроенные векторы тем, документов и слов.
- BERTopic — библиотека тематического моделирования, использующая модели BERT и c-TF-IDF для создания плотных кластеров, позволяющих легко интерпретировать темы, сохраняя важные слова в описаниях тем.
- Greykite — библиотека прогнозирования создает гибкие, интуитивно понятные и быстрые прогнозы с помощью флагманского алгоритма Silverkite.
- Kats — набор инструментов для анализа временных рядов.
- Merlion — комплексная библиотека машинного обучения с поддержкой многозадачности для загрузки и преобразования данных, построения и обучения моделей, постобработки выходных данных модели и оценки производительности модели.
- Spice.ai — библиотека для разработки интеллектуальных приложений Python, использующих данные временных рядов.
- TorchGeo — библиотека на базе PyTorch, которая предоставляет наборы данных, преобразования, сэмплеры и предварительно обученные модели, специфичные для геопространственных данных.
- PyOpenGL — одна из самых известных и полезных библиотек для создания визуальной графики и 3D игр на Python.
- pedalboard — библиотека Spotify для программного добавления эффектов к аудио.
- SpeechBrain — универсальный набор инструментов для работы с речью на основе PyTorch.
- PyTorch Metric Learning — модульная, гибкая и расширяемая библиотека добавляет глубокое метрическое обучение в приложения Python.
- Gradio — библиотека легко превращает сценарии данных в общедоступные веб-приложения. По своему функционалу похожа на популярную ML-библиотеку Streamlit, но гораздо проще в использовании.
- Hyperactive — набор инструментов для оптимизации и сбора данных при прототипировании моделей, требующих значительных вычислительных ресурсов.
- Gradient-Free-Optimizers — простая и надежная оптимизация данных с использованием локальных, глобальных, популяционных и последовательных методов в числовых дискретных пространствах поиска.
- Ecco — библиотека для визуализации и исследования языковых моделей NLP.
- UpliftML — библиотека для uplift-моделирования. Отлично подходит для изучения причинно-следственных связей в персонализации/маркетинге.
- AutoScraper — библиотека для парсинга в автоматическом режиме, устраняющая необходимость написания/поддержки селекторов или выражений XPath.
- BeautifulSoup — библиотека входит в число лучших инструментов Python для парсинга сайтов и веб-страниц.
- Aim — простой в использовании и производительный трекер экспериментов с открытым исходным кодом.
- NeuralProphet — модель временных рядов на основе нейтронной сети, вдохновленная Facebook Prophet и AR-Net, построенная на PyTorch.
Заключение
При выборе фреймворка или библиотеки Python нужно учитывать не только возможности самого инструмента, но, прежде всего, размер и сложность продукта, который будет создаваться с его помощью. Особенно это важно для масштабных проектов, требующих работы на нескольких веб-серверах, обработки огромных объемов трафика и поддержки новых дополнительных функций для улучшения функциональности кода.
Если нужно разработать большое приложение со сложной структурой и множеством функций, правильным выбором будет многофункциональный фулстек-фреймворк или библиотеки с собственной экосистемой, как PyTorch. Если же планируется создать небольшое и простое приложение, стоит подумать о микрофреймворках или отдельных узкоспециализированных библиотеках.
Нужна надёжная база для разработки программных продуктов на Python? Выбирайте виртуальные серверы от Eternalhost с технической поддержкой 24/7 и бесплатной защитой от DDoS!
Работа с файлами

На практике в реальных проектах Data Science часто приходится сталкиваться с чтением датасетов, а также записывать добытую в ходе вычислений информацию в файлы. Сегодня мы расскажем о работе с файлами в Python: чтение и запись, проблема с кодировками, добавление значений в конец файла, временные папки и файлы.
Открываем, а затем читаем или записываем
Предположим, у нас имеется файл, который нужно прочитать в Python. Для этого можно воспользоваться функцией open внутри контекстного менеджера:
with open('file.txt') as f: data = f.read() # содержимое файла
Таким же образом можно записать информацию в файл, указав w в качестве аргумента:
text = 'Hello' with open('file.txt', 'w') as f: f.write(text)
Отметим некоторые особенности данной функции. Во-первых, для чтения файла мы не указывали никаких аргументов кроме имени файла, поскольку по умолчанию уже стоит режим чтения. Мы также не указывали явно, что это именно текстовый файл, а не бинарный, так как это тоже стоит по умолчанию. Для чтения и записи бинарных файлов добавляется b , например, rb или wb .
Во-вторых, мы использовали функцию open в контекстном менеджере. Можно обойтись и без него, но тогда после чтения или записи следует закрыть файл.
f = open('file.txt') f.read() f.close()
На открытие файла Python выделяет память, поэтому, чтобы избежать ее утечки, рекомендуется закрывать файлы.
Чтение файла с разной кодировкой
На многих операционных системах Python в качестве стандарта кодирования использует UTF-8, который также поддерживает кириллицу. Тем не менее, часто можно столкнуться с проблемами неправильной кодировки и получить распространенную ошибку вроде этой:
>>> f = open('somefile.txt', encoding='ascii') >>> f.read() Traceback (most recent call last): File "", line 1, in File "/usr/local/lib/Python3.8/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 12: ordinal not in range(128))
В примере указана кодировка ASCII, но файл закодирован в другом формате, поэтому и возникает такая ошибка. Решить ее можно тремя способами:
-
Указать erorr=replace , который заменит нераспознанные символы знаком ? :
>>> f = open('somefile.txt', encoding='ascii', errors='replace') >>> f.read() 'H?llo py?ho?-school!'
>>> f = open('somefile.txt', encoding='ascii', errors='replace') >>> f.read() 'Hllo pyho-school!'
f = open('somefile.txt', encoding='utf-8') # или cp1251 f = open('somefile.txt', encoding='cp1251')
Добавление в конец и запрет открытия файлов
Как мы уже отметили ранее, для записи текстового файла добавляется аргумент w . Но если вызвать метод write, он перепишет весь файл. Во многих случаях требуется добавить данные в конец файла. Тогда используется a вместо w :
text2 = 'world' with open('file.txt', 'a') as f: f.write(text) # Helloworld
Если файла не существует, то при a и при w он будет создан. Но чтобы не трогать существующие файлы, а создать новый, передается параметр x :
# 'x' не даст возможности открыть файл, так как он существует >>> with open('file.txt', 'x') as f: . f.write(text2) FileExistsError Traceback (most recent call last) FileExistsError: [Errno 17] File exists: 'file.txt' # Поскольку file2.txt не существует, все OK >>> with open('file2.txt', 'x') as f: . f.write(text2)
Временные файлы
Иногда бывает, что требуется создать файл или папку внутри Python-программы, а после ее закрытия их нужно удалить. Тогда пригодится стандартный модуль tempfile. Например, класс TemporaryFile создаст временный файл, который удалится после закрытия. Ниже пример в Python.
>>> from tempfile import TemporaryFile >>> f = TemporaryFile("w+t") >>> f.write("hello") >>> f.seek(0) >>> f.read() 'hello' >>> f.close() # файл уничтожается # либо в контекстном менеджере f.write(text2)
Обратите внимание на 3 вещи. Первое, мы явно передаем «w+t» , чтобы записать как текстовый файл, поскольку по умолчанию стоит «w+b» для бинарных файлов. Второе, метод seek(0) используется для перехода на самый первый символ, поскольку чтение происходит с текущего указателя, а он стоит в конце (после буквы ‘o’ в слове ‘hello’). Поэтому не стоит переживать, что мы можем стереть предыдущую запись:
>>> f.seek(5) # переходим в конец >>> f.read() '' >>> f.write("world") 5 >>> f.seek(0) # переходим в начало >>> f.read() 'helloworld'
Третье, файл TemporaryFile невидим для файловой системы, он используется только внутри Python, поэтому извне будет трудно его найти.
Именованные временные файлы
А вот объекты класса NamedTemporaryFile будут видны файловой системе, и найти месторасположение можно с помощью атрибута name :
>>> from tempfile import NamedTemporaryFile >>> f = NamedTemporaryFile("w+t") >>> f.name '/tmp/tmp60djsgli' >>> f.close()
Как можно заметить, файл называется tmp60djsgli . Для удобства можно явно указать его название и формат:
>>> f = NamedTemporaryFile("w+t", prefix="myfile", suffix=".txt") >>> f.name '/tmp/myfile7mxae0fi.txt'
Временные папки
Кроме временных файлов можно создавать временные папки. Для этого используется класс TemporaryDirectory :
>>> from tempfile import TemporaryDirectory >>> d = TemporaryDirectory() >>> d.name '/tmp/tmp5eadqzz5'
Он также принимает в качестве аргументов prefix и suffix , а также может использоваться внутри контекстного менеджера Python.
В следующей статье поговорим о взаимодействии файловой системы и Python. А получить практические навыки работы с файлами на реальных проектах Data Science вы сможете на наших курсах по Python в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.
- https://docs.python.org/3/library/functions.html#open
- https://docs.python.org/3/library/tempfile.html
Создание Python-обвязки для библиотек, написанных на C/C++, с помощью SIP. Часть 1
Иногда во время работы над проектом на языке Python возникает желание использовать библиотеку, которая написана не на Python, а, например, на C или C++. Причины для этого могут быть разные Во-первых, Python — язык замечательный, но в некоторых ситуациях недостаточно быстрый. И если вы видите, что производительность ограничена особенностями языка Python, то имеет смысл часть программы написать на другом языке (в этой статье мы будем говорить про C и C++), оформить эту часть программы в виде библиотеки, сделать Python-обвязки (Python bindings) поверх нее и использовать полученный таким образом модуль как обычную Python-библиотеку. Во-вторых, часто случается ситуация, когда вы знаете, что есть библиотека, которая решает требуемую задачу, но, к сожалению, эта библиотека написана не на Python, а на тех же C или C++. В этом случае также мы можем сделать Python-обвязку над библиотекой и пользоваться ей, не задумываясь о том, что библиотека изначально не была написана на Python.
Для создания Python-обвязок существуют разные инструменты, начиная от более низкоуровневых вроде Python/C API и до более высокоуровневых вроде SWIG и SIP.
У меня не было цели сравнения разных способов создания Python-обвязок, а хотелось бы рассказать об основах использования одного инструмента, а именно SIP. Изначально SIP разрабатывался для создания обвязки вокруг библиотеки Qt — PyQt, а также используется при разработке других крупных Python-библиотек, например, wxPython.
В этой статье в качестве компилятора для C будет использоваться gcc, а в качестве компилятора C++ — g++. Все примеры проверялись под Arch Linux и Python 3.8. Для того, чтобы не усложнять примеры, тема компиляции под разные операционные системы и с помощью разных компиляторов (например, Visual Studio) не входит в рамки этой статьи.
Все примеры для данной статьи вы можете скачать из репозитория на github.
Репозиторий с исходниками SIP расположен по адресу https://www.riverbankcomputing.com/hg/sip/. В качестве системы контроля версий для SIP используется Mercurial.
Делаем обвязку над библиотекой на языке C
Пишем библиотеку на C
Этот пример находится в папке pyfoo_c_01 в исходниках, но в данной статье мы будем подразумевать, что мы все делаем с чистого листа.
Начнем с простого примера. Для начала сделаем простую C-библиотеку, которую потом будем запускать из скрипта на Python. Пусть в нашей библиотеке будет единственная функция
int foo(char*);
которая будет принимать строку и возвращать ее длину, умноженную на 2.
Заголовочный файл foo.h может выглядеть, например, так:
#ifndef FOO_LIB #define FOO_LIB int foo(char* str); #endif
И файл с реализацией foo.cpp:
#include #include "foo.h" int foo(char* str)
Для проверки работоспособности библиотеки напишем простую программу main.c:
#include #include "foo.h" int main(int argc, char* argv[])
Для аккуратности создадим Makefile:
CC=gcc CFLAGS=-c DIR_OUT=bin all: main main: main.o libfoo.a $(CC) $(DIR_OUT)/main.o -L$(DIR_OUT) -lfoo -o $(DIR_OUT)/main main.o: makedir main.c $(CC) $(CFLAGS) main.c -o $(DIR_OUT)/main.o libfoo.a: makedir foo.c $(CC) $(CFLAGS) foo.c -o $(DIR_OUT)/foo.o ar rcs $(DIR_OUT)/libfoo.a $(DIR_OUT)/foo.o makedir: mkdir -p $(DIR_OUT) clean: rm -rf $(DIR_OUT)/*
Пусть все исходники библиотеки foo расположены в подпапке foo в папке с исходниками:
foo_c_01/ └── foo ├── foo.c ├── foo.h ├── main.c └── Makefile
Заходим в папку foo и компилируем исходники с помощью команды
make
В процессе компиляции будет выведен текст
mkdir -p bin gcc -c main.c -o bin/main.o gcc -c foo.c -o bin/foo.o ar rcs bin/libfoo.a bin/foo.o gcc bin/main.o -Lbin -lfoo -o bin/main
Результат компиляции будет помещен в папку bin внутри папки foo:
foo_c_01/ └── foo ├── bin │ ├── foo.o │ ├── libfoo.a │ ├── main │ └── main.o ├── foo.c ├── foo.h ├── main.c └── Makefile
Мы скомпилировали библиотеку для статической линковки и программу, которая ее использует под названием main. После компиляции можно убедиться, что программа main запускается.
Давайте сделаем Python-обвязку над библиотекой foo.
Основы работы с SIP
Для начала SIP нужно установить. Делается это стандартно, как и для всех остальных библиотек с помощью pip:
pip install --user sip
Разумеется, если вы работаете в виртуальном окружении, то параметр —user, сообщающий о том, что библиотеку SIP нужно установить в папку пользователя, а не глобально в систему, указывать не надо.
Что нам нужно сделать, чтобы библиотеку foo можно было бы вызывать из кода на Python? Как минимум нужно создать два файла: один из них в формате TOML и назвать его pyproject.toml, а второй — файл с расширением .sip. Давайте последовательно разбираться с каждым из них.
Нам нужно договориться о структуре исходников. Внутри папки pyfoo_c содержится папка foo, в которой расположены исходники для библиотеки. После компиляции внутри папки foo создается папка bin, которая будет содержать все скомпилированные файлы. Позже мы добавим возможность пользователю указывать пути до заголовочных и объектных файлов библиотеки через командную строку.
Файлы, необходимые для SIP, будут расположены в той же папке, что и папка foo.
pyproject.toml
Файл pyproject.toml — это не изобретение разработчиков SIP, а формат описания проекта на языке Python, описанный в PEP 517 «A build-system independent format for source trees» и в PEP 518 «Specifying Minimum Build System Requirements for Python Projects». Это файл в формате TOML, который можно рассматривать как более продвинутую версию формата ini, в котором параметры хранятся в виде «ключ=значение», при этом параметры могут располагаться не просто в разделах вроде [foo], которые в терминах TOML называются таблицами, но и в подразделах вида [foo.bar.spam]. Параметры могут могут содержать в качестве значения не только строки, но и списки, числа и булевы значения.
Этот файл по задумке должен описывать все, что необходимо для сборки Python-пакета, причем не обязательно с помощью SIP. Правда, как мы увидим чуть позже, этого файла в некоторых случаях будет не достаточно, и ему в дополнение нужно будет создать небольшой скрипт на Python. Но давайте обо всем по порядку.
Полное описание всех возможных параметров файла pyproject.toml, которые относятся к SIP, можно найти на странице документации SIP.
Создадим для нашего примера файл pyproject.toml на том же уровне, что и папка foo:
foo_c_01/ ├── foo │ ├── bin │ │ ├── foo.o │ │ ├── libfoo.a │ │ ├── main │ │ └── main.o │ ├── foo.c │ ├── foo.h │ ├── main.c │ └── Makefile └── pyproject.toml
Содержимое pyproject.toml будет следующее:
[build-system] requires = ["sip >=5,
Раздел [build-system] («таблица» в терминах TOML) является стандартным и описан в PEP 518. Он содержит два параметра:
- requires — список пакетов, необходимых для сборки нашего пакета. Формат описания зависимостей пакета описан в PEP 508 «Dependency specification for Python Software Packages». В данном случае нам требуется только пакет sip версии 5.x.
- build-backend описывает, с помощью чего мы будем собирать наш пакет. Строго говоря, этот параметр в виде строки должен содержать полное название Python-объекта, который будет заниматься сборкой. Если не задумываться над глубоким содержимым этого параметра, то для пакетов, собираемых с помощью SIP, это значение должно равняться «sipbuild.api».
Раздел [tool.sip.metadata] содержит общую информацию о пакете: имя собираемого пакета (у нас пакет будет называться pyfoo, но не путайте это имя с именем модуля, который мы потом будем импортировать в Python), номер версии пакета (в нашем случае номер версии «0.1») и лицензия (например, "MIT").
Самое важное с точки зрения сборки описано в разделе [tool.sip.bindings.pyfoo].
Обратите внимание на имя пакета в заголовке раздела. В этот раздел мы добавили два параметра:
- headers — список заголовочных файлов, которые необходимы для использования библиотеки foo.
- libraries — список объектных файлов, скомпилированных для статической линковки.
- include-dirs — путь, где искать дополнительные заголовочные файлы помимо тех, что прилагаются к компилятору C. В данном случае, где искать файл foo.h.
- library-dirs — путь, где искать дополнительные объектные файлы помимо тех, что прилагаются к компилятору C. В данном случае это папка, в которой создается скомпилированный файл библиотеки foo.
pyfoo.sip
Создадим файл pyfoo.sip в той же папке, что и файл pyproject.toml:
foo_c_01/ ├── foo │ ├── bin │ │ ├── foo.o │ │ ├── libfoo.a │ │ ├── main │ │ └── main.o │ ├── foo.c │ ├── foo.h │ ├── main.c │ └── Makefile ├── pyfoo.sip └── pyproject.toml
Файл с расширением .sip описывает интерфейс исходной библиотеки, который будет преобразован в модуль на Python. Этот файл имеет собственный формат, который мы сейчас рассмотрим, и напоминает заголовочный файл C/C++ с дополнительной разметкой, которая должна помочь SIP создать Python-модуль.
В нашем примере этот файл должен называться pyfoo.sip, потому что до этого в файле pyproject.toml мы создали раздел [tool.sip.bindings.pyfoo]. В общем случае таких разделов может быть несколько и, соответственно, должно быть несколько файлов *.sip. Но если у нас несколько sip-файлов, то это особый случай с точки зрения SIP, и в этой статье мы его не рассматриваем. Обратите внимание, что в общем случае имя файла .sip (и, соответственно, имя раздела) может не совпадать с именем пакета, которое указано в параметре name в разделе [tool.sip.metadata].
Рассмотрим файл pyfoo.sip из нашего примера:
%Module(name=foo, language="C") int foo(char*);
Строки, которые начинаются с символа "%", называются директивами. Они должны подсказывать SIP, как нужно правильно собирать и оформлять Python-модуль. Полный список директив описан на этой странице документации. Некоторые директивы имеют дополнительные параметры. Параметры могут быть не обязательными.
В этом примере мы используем две директивы, с некоторыми другими директивами познакомимся в следующих примерах.
Файл pyfoo.sip начинается с директивы %Module(name=foo, language=«C»). Обратите внимание, что значение первого параметра (name) мы указали без кавычек, а значение второго параметра (language) с кавычками, как строки в C/C++. Это требование данной директивы, описанное в документации к директиве %Module.
В директиве %Module обязательным является только параметр name, который задает имя Python-модуля, из которого мы будем импортировать функцию библиотеки. В данном случае модуль называется foo, он будет содержать функцию foo, поэтому после сборки и установки мы будем ее импортировать с помощью кода:
from foo import foo
Мы могли бы сделать этот модуль вложенным в другой модуль, заменив эту строку, например, такой:
%Module(name=foo.bar, language="C") .
Тогда импортировать функцию foo нужно было бы следующим образом:
from foo.bar import foo
Параметр language директивы %Module указывает язык, на котором написана исходная библиотека. Значение этого параметра может быть либо «C», либо «C++». Если этот параметр не указать, то SIP будет считать, что библиотека написана на C++.
Теперь посмотрим на последнюю строчку файла pyfoo.sip:
int foo(char*);
Это описание интерфейса функции из библиотеки, которую мы хотим поместить в Python-модуль. На основе этого объявления sip создаст Python-функцию. Думаю, что здесь все должно быть ясно.
Собираем и проверяем
Теперь все готово для того, чтобы собрать Python-пакет с обвязкой для библиотеки на C. В первую очередь нужно собрать саму библиотеку. Переходим в папку pyfoo_c_01/foo/ и запускаем сборку с помощью команды make:
$ make mkdir -p bin gcc -c main.c -o bin/main.o gcc -c foo.c -o bin/foo.o ar rcs bin/libfoo.a bin/foo.o gcc bin/main.o -Lbin -lfoo -o bin/main
Если все прошло успешно, то внутри папки foo будет создана папка bin, в котором среди прочих файлов будет собранная библиотека libfoo.a. Напомню, что здесь, чтобы не отвлекаться от основной темы, мы говорим только про сборку под Linux с помощью gcc.
Переходим обратно в папку pyfoo_c_01. Теперь пришло время познакомиться с командами SIP. После установки SIP станут доступны следующие команды командной строки (страница документации):
- sip-build. Создает объектный файл Python-расширения (Python extension).
- sip-install. Создает объектный файл Python-расширения и устанавливает его.
- sip-sdist. Создает пакет в виде архива .tar.gz, который можно установить с помощью pip.
- sip-wheel. Создает пакет в формате wheel (файл с расширением .whl).
- sip-module. Создает модуль, в который включается только служебные инструменты, необходимые самому SIP. Это нужно, если вы создаете библиотеку, разбитую на несколько пакетов. В этой статье мы не будем рассматривать такой случай, мы будем создавать только так называемый standalone project, то есть наш пакет будет единый, он будет включать и библиотеку, для которой мы делаем обвязку, и все служебные инструменты.
- sip-distinfo. Создает и заполняет папку .dist-info, которая используется в пакете в формате wheel.
Для начала, чтобы лучше понять работу SIP, запустим команду sip-build, причем с параметром --verbose для более подробного вывода в консоль, и посмотрим, что происходит в процессе сборки.
$ sip-build --verbose
These bindings will be built: pyfoo.
Generating the pyfoo bindings…
Compiling the 'foo' module…
building 'foo' extension
creating build
creating build/temp.linux-x86_64-3.8
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c sipfoocmodule.c -o build/temp.linux-x86_64-3.8/sipfoocmodule.o
sipfoocmodule.c: В функции «func_foo»:
sipfoocmodule.c:29:22: предупреждение: неявная декларация функции «foo» [-Wimplicit-function-declaration]
29 | sipRes = foo(a0);
| ^~~
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c array.c -o build/temp.linux-x86_64-3.8/array.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c bool.cpp -o build/temp.linux-x86_64-3.8/bool.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c objmap.c -o build/temp.linux-x86_64-3.8/objmap.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c qtlib.c -o build/temp.linux-x86_64-3.8/qtlib.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c int_convertors.c -o build/temp.linux-x86_64-3.8/int_convertors.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c voidptr.c -o build/temp.linux-x86_64-3.8/voidptr.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c apiversions.c -o build/temp.linux-x86_64-3.8/apiversions.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c descriptors.c -o build/temp.linux-x86_64-3.8/descriptors.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c threads.c -o build/temp.linux-x86_64-3.8/threads.o
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fno-semantic-interposition -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -march=x86-64 -mtune=generic -O3 -pipe -fno-plt -fPIC -DSIP_PROTECTED_IS_PUBLIC -Dprotected=public -I. -I../../foo -I/usr/include/python3.8 -c siplib.c -o build/temp.linux-x86_64-3.8/siplib.o
siplib.c: В функции «slot_richcompare»:
siplib.c:9536:16: предупреждение: «st», возможно, используется без инициализации в данной функции [-Wmaybe-uninitialized]
9536 | slot = findSlotInClass(ctd, st);
| ^~~~~~~~~~~~~~~~~~~~~~~~
siplib.c:10671:19: замечание: «st» было объявлено здесь
10671 | sipPySlotType st;
| ^~
siplib.c: В функции «parsePass2»:
siplib.c:5625:32: предупреждение: «owner», возможно, используется без инициализации в данной функции [-Wmaybe-uninitialized]
5625 | *owner = arg;
| ~~~~~~~^~
g++ -pthread -shared -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -fno-semantic-interposition -Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now build/temp.linux-x86_64-3.8/sipfoocmodule.o build/temp.linux-x86_64-3.8/array.o build/temp.linux-x86_64-3.8/bool.o build/temp.linux-x86_64-3.8/objmap.o build/temp.linux-x86_64-3.8/qtlib.o build/temp.linux-x86_64-3.8/int_convertors.o build/temp.linux-x86_64-3.8/voidptr.o build/temp.linux-x86_64-3.8/apiversions.o build/temp.linux-x86_64-3.8/descriptors.o build/temp.linux-x86_64-3.8/threads.o build/temp.linux-x86_64-3.8/siplib.o -L../../foo/bin -L/usr/lib -lfoo -o /home/jenyay/projects/soft/sip-examples/pyfoo_c_01/build/foo/foo.cpython-38-x86_64-linux-gnu.so
The project has been built.
Мы не будем сильно углубляться в работу SIP, но из вывода видно, что происходит компиляция каких-то исходников. Эти исходники можно увидеть в созданной этой командой папке build/foo/:
pyfoo_c_01 ├── build │ └── foo │ ├── apiversions.c │ ├── array.c │ ├── array.h │ ├── bool.cpp │ ├── build │ │ └── temp.linux-x86_64-3.8 │ │ ├── apiversions.o │ │ ├── array.o │ │ ├── bool.o │ │ ├── descriptors.o │ │ ├── int_convertors.o │ │ ├── objmap.o │ │ ├── qtlib.o │ │ ├── sipfoocmodule.o │ │ ├── siplib.o │ │ ├── threads.o │ │ └── voidptr.o │ ├── descriptors.c │ ├── foo.cpython-38-x86_64-linux-gnu.so │ ├── int_convertors.c │ ├── objmap.c │ ├── qtlib.c │ ├── sipAPIfoo.h │ ├── sipfoocmodule.c │ ├── sip.h │ ├── sipint.h │ ├── siplib.c │ ├── threads.c │ └── voidptr.c ├── foo │ ├── bin │ │ ├── foo.o │ │ ├── libfoo.a │ │ ├── main │ │ └── main.o │ ├── foo.c │ ├── foo.h │ ├── main.c │ └── Makefile ├── pyfoo.sip └── pyproject.toml
В папке build/foo появились вспомогательные исходники. Из любопытства посмотрим файл sipfoocmodule.c, поскольку он непосредственно относится к модулю foo, который будет создан:
/* * Module code. * * Generated by SIP 5.1.1 */ #include "sipAPIfoo.h" /* Define the strings used by this module. */ const char sipStrings_foo[] = < 'f', 'o', 'o', 0, >; PyDoc_STRVAR(doc_foo, "foo(str) -> int"); static PyObject *func_foo(PyObject *sipSelf,PyObject *sipArgs) < PyObject *sipParseErr = SIP_NULLPTR; < char* a0; if (sipParseArgs(&sipParseErr, sipArgs, "s", &a0)) < int sipRes; sipRes = foo(a0); return PyLong_FromLong(sipRes); >> /* Raise an exception if the arguments couldn't be parsed. */ sipNoFunction(sipParseErr, sipName_foo, doc_foo); return SIP_NULLPTR; > /* This defines this module. */ sipExportedModuleDef sipModuleAPI_foo = < 0, SIP_ABI_MINOR_VERSION, sipNameNr_foo, 0, sipStrings_foo, SIP_NULLPTR, SIP_NULLPTR, 0, SIP_NULLPTR, SIP_NULLPTR, 0, SIP_NULLPTR, 0, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, , SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR >; /* The SIP API and the APIs of any imported modules. */ const sipAPIDef *sipAPI_foo; /* The Python module initialisation function. */ #if defined(SIP_STATIC_MODULE) PyObject *PyInit_foo(void) #else PyMODINIT_FUNC PyInit_foo(void) #endif < static PyMethodDef sip_methods[] = < , >; static PyModuleDef sip_module_def = < PyModuleDef_HEAD_INIT, "foo", SIP_NULLPTR, -1, sip_methods, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR, SIP_NULLPTR >; PyObject *sipModule, *sipModuleDict; /* Initialise the module and get it's dictionary. */ if ((sipModule = PyModule_Create(&sip_module_def)) == SIP_NULLPTR) return SIP_NULLPTR; sipModuleDict = PyModule_GetDict(sipModule); if ((sipAPI_foo = sip_init_library(sipModuleDict)) == SIP_NULLPTR) return SIP_NULLPTR; /* Export the module and publish it's API. */ if (sipExportModule(&sipModuleAPI_foo, SIP_ABI_MAJOR_VERSION, SIP_ABI_MINOR_VERSION, 0) < 0) < Py_DECREF(sipModule); return SIP_NULLPTR; >/* Initialise the module now all its dependencies have been set up. */ if (sipInitModule(&sipModuleAPI_foo,sipModuleDict) < 0) < Py_DECREF(sipModule); return SIP_NULLPTR; >return sipModule; >
Если вы работали с Python/C API, то увидите знакомые функции. Особо обратите внимание на функцию func_foo, начинающейся с 18 строки.
В результате компиляции этих исходников будет создан файл build/foo/foo.cpython-38-x86_64-linux-gnu.so, именно он и содержит Python-расширение, которое еще нужно правильно установить.
Для того, чтобы одной командой скомпилировать расширение и сразу его установить, можно воспользоваться командой sip-install, но мы ей пользоваться не будем, потому что по умолчанию пытается установить созданное Python-расширение глобально в систему. У этой команды есть параметр --target-dir, с помощью которого можно указать путь, куда нужно устанавливать расширение, но мы лучше воспользуемся другими инструментами, создающими пакеты, которые затем можно будет установить с помощью pip.
Сначала воспользуемся командой sip-sdist. Использовать ее очень просто:
$ sip-sdist The sdist has been built.
После этого будет создан файл pyfoo-0.1.tar.gz, который можно установить с помощью команды:
pip install --user pyfoo-0.1.tar.gz
В результате будет показана следующая информация и пакет установится:
Processing ./pyfoo-0.1.tar.gz Installing build dependencies . done Getting requirements to build wheel . done Preparing wheel metadata . done Building wheels for collected packages: pyfoo Building wheel for pyfoo (PEP 517) . done Created wheel for pyfoo: filename=pyfoo-0.1-cp38-cp38-manylinux1_x86_64.whl size=337289 sha256=762fc578. Stored in directory: /home/jenyay/.cache/pip/wheels/54/dc/d8/cc534fff. Successfully built pyfoo Installing collected packages: pyfoo Attempting uninstall: pyfoo Found existing installation: pyfoo 0.1 Uninstalling pyfoo-0.1: Successfully uninstalled pyfoo-0.1 Successfully installed pyfoo-0.1
Давайте убедимся, что нам удалось сделать Python-обвязку. Запускаем Python и пытаемся вызвать функцию. Напомню, что согласно нашим настройкам, пакет pyfoo содержит модуль foo, в котором имеется функция foo.
>>> from foo import foo >>> foo(b'123456') 12
Обратите внимание, что в качестве параметра функции мы передаем не просто строку, а строку байтов b'123456' — прямой аналог char* в C. Чуть позже мы добавим преобразование char* в str и обратно. Результат получился ожидаемым. Напомню, что функция foo возвращает удвоенный размер массива типа char*, переданного ей в качестве параметра.
Давайте попробуем передать в функцию foo обычную Python-строку вместо списка байтов.
>>> from foo import foo >>> foo('123456') Traceback (most recent call last): File "", line 1, in TypeError: foo(str): argument 1 has unexpected type 'str'
Созданная обвязка не смогла преобразовать строку в char*, как ее научить это делать, мы рассмотрим в следующем разделе.
Поздравляю, мы сделали первую обвязку над библиотекой, написанной на языке C.
Выйдем из интерпретатора Python и соберем сборку в формате wheel. Как вы скорее всего знаете, wheel — это сравнительно новый формат пакетов, который в последнее время используется повсеместно. Описание формата содержится в PEP 427 «The Wheel Binary Package Format 1.0», но описание особенностей формата wheel — тема, достойная отдельной большой статьи. Для нас важно, что пакет в формате wheel пользователь может легко установить с помощью pip.
Пакет в формате wheel собирается ничуть не сложнее, чем пакет в формате sdist. Для этого в папке с файлом pyproject.toml нужно выполнить команду
sip-wheel
После запуска этой команды будет показан процесс сборки и могут быть предупреждения от компилятора:
These bindings will be built: pyfoo.
Generating the pyfoo bindings…
Compiling the 'foo' module…
sipfoocmodule.c: В функции «func_foo»:
sipfoocmodule.c:29:22: предупреждение: неявная декларация функции «foo» [-Wimplicit-function-declaration]
29 | sipRes = foo(a0);
| ^~~
siplib.c: В функции «slot_richcompare»:
siplib.c:9536:16: предупреждение: «st», возможно, используется без инициализации в данной функции [-Wmaybe-uninitialized]
9536 | slot = findSlotInClass(ctd, st);
| ^~~~~~~~~~~~~~~~~~~~~~~~
siplib.c:10671:19: замечание: «st» было объявлено здесь
10671 | sipPySlotType st;
| ^~
siplib.c: В функции «parsePass2»:
siplib.c:5625:32: предупреждение: «owner», возможно, используется без инициализации в данной функции [-Wmaybe-uninitialized]
5625 | *owner = arg;
| ~~~~~~~^~
The wheel has been built.
Когда сборка завершится (наш маленький проект компилируется быстро), в папке проекта появится файл с именем pyfoo-0.1-cp38-cp38-manylinux1_x86_64.whl или похожим. Имя созданного файла может отличаться в зависимости от вашей операционной системы и версии Python.
Теперь мы можем установить этот пакет с помощью pip:
pip install --user --upgrade pyfoo-0.1-cp38-cp38-manylinux1_x86_64.whl
Здесь используется параметр --upgrade, чтобы pip заменил модуль pyfoo, установленный ранее.
Дальше модуль foo и пакета pyfoo можно использовать, как было показано выше.
Добавляем правила преобразования в char*
В предыдущем разделе мы столкнулись с проблемой, что функция foo может принимать только набор байтов, но не строки. Сейчас мы исправим этот недостаток. Для этого мы воспользуемся еще одним инструментом SIP — аннотациями. Аннотации используются внутри файлов .sip и применяются к каким-то элементам кода: функциям, классам, аргументам функций, исключениям, переменным и др. Аннотации записываются между прямыми слешами: /аннотация/.
Аннотация может работать в качестве флага, который может находиться в состоянии установлен или не установлен, например: /ReleaseGIL/, или некоторым аннотациям нужно присваивать какие-либо значения, например: /Encoding=«UTF-8»/. Если к какому-то объекту нужно применить несколько аннотаций, то они разделяются запятыми внутри слешей: /аннотация_1, аннотация_2/.
В следующем примере, который находится в папке pyfoo_c_02, добавим в файл pyfoo.sip аннотацию для параметра функции foo:
%Module(name=foo, language="C") int foo(char* /Encoding="UTF-8"/);
Аннотация Encoding указывает, в какую кодировку должна быть закодирована строка, которая будет передаваться в функцию. Значения этой аннотации могут быть следующие: «ASCII», «Latin-1», «UTF-8» или «None». Если аннотация Encoding не указана или равна None, то параметр для такой функции не подвергается никакой кодировке и передается в функцию как есть, но в этом случае параметр в коде на Python должен тип bytes, т.е. массив байтов, что мы и видели в предыдущем примере. Если кодировка указана, то этот параметр может быть строкой (типом str в Python). Аннотация Encoding может применяться только к параметрам типа char, const char, char* или const char*.
Проверим, как теперь работает функция foo из модуля foo. Для этого, как и ранее, нужно сначала скомпилировать библиотеку foo, вызвав внутри папки foo команду make, а затем из папки примера pyfoo_c_02 вызвать команду, например, sip-wheel. Будет создан файл pyfoo-0.2-cp38-cp38-manylinux1_x86_64.whl или с похожим названием, который можно установить с помощью команды:
pip install --user --upgrade pyfoo-0.2-cp38-cp38-manylinux1_x86_64.whl
Если все прошло успешно, запускаем интерпретатор Python и пробуем вызвать функцию foo со строковым аргументом:
>>> from foo import foo >>> foo(b'qwerty') 12 >>> foo('qwerty') 12 >>> foo('йцукен') 24
Сначала мы убеждаемся, что использование типа bytes по-прежнему возможно. После этого убеждаемся, что теперь мы можем передавать в функцию foo также и строковые аргументы. Обратите внимание, что функция foo для строкового аргумента с русскими буквами вернула значение в два раза больше, чем для строки, содержащей только латинские буквы. Это произошло из-за того, что функция foo считает не длину строки в символах (и удваивает ее), а длину массива char*, а поскольку в кодировке UTF-8 русские буквы занимают 2 байта, то и размер массива char* после преобразования из строки Python получился в два раза длиннее.
Отлично! Мы решили проблему с аргументом функции foo, но что, если у нас в библиотеке будут десятки или сотни таких функций, для каждой из них придется указывать кодировку параметров? Часто кодировка в программе используется одна и та же, и нет цели для разных функций указывать разные кодировки. В этом случае в SIP есть возможность указать кодировку по умолчанию, а если для какой-то функции кодировка нужна какая-то другая, то ее можно переопределить с помощью аннотации Encoding.
Чтобы задать кодировку параметров функции по умолчанию предназначена директива %DefaultEncoding. Ее использование показано в примере, расположенном в папке pyfoo_c_03.
Для того, чтобы воспользоваться директивой %DefaultEncoding, изменим файл pyfoo.sip, теперь его содержимое выглядит следующим образом:
%Module(name=foo, language="C") %DefaultEncoding "UTF-8" int foo(char*);
Теперь, если у аргумента функции типа char, char* и т.п. нет аннотации Encoding, то кодировка берется из директивы %DefaultEncoding, а если ее нет, то преобразование не производится, и для всех параметров char* и т.п. нужно передавать не строки, а bytes.
Пример из папки pyfoo_c_03 собирается и проверяется так же, как и пример из папки pyfoo_c_02.
Коротко о project.py. Автоматизируем сборку
До сих пор для создания Python-обвязки мы использовали два служебных файла — pyproject.toml и pyfoo.sip. Теперь мы познакомимся с еще одним таким файлом, который должен называться project.py. С помощью этого скрипта мы можем влиять на процесс сборки нашего пакета. Давайте займемся автоматизацией сборки. Для того, чтобы собрать примеры pyfoo_c_01 — pyfoo_c_03 из предыдущих разделов, нужно было сначала зайти в папку foo/, выполнить там компиляцию с помощью команды make, вернуться в папку, где расположен файл pyproject.toml и только тогда запустить сборку пакета с помощью одной из команд sip-*.
Теперь наша цель — сделать так, чтобы при выполнении команд sip-build, sip-sdist и sip-wheel сначала запускалась сборка C-библиотеки foo, а потом уже запускалась непосредственно сама команда.
Пример, создаваемый в этом разделе, находится в папке pyfoo_c_04 исходников.
Чтобы изменить процесс сборки, мы можем в файле project.py (имя файла должно быть именно таким) объявить класс, производный от класса sipbuild.Project. У этого класса есть методы, которые мы можем переопределить на свои. В данный момент нас интересуют следующие методы:
- build. Вызывается в процессе вызова команды sip-build.
- build_sdist. Вызывается в процессе вызова команды sip-sdist.
- build_wheel. Вызывается в процессе вызова команды sip-wheel.
- install. Вызывается в процессе вызова команды sip-install.
Создадим файл project.py со следующим содержимым:
import os import subprocess from sipbuild import Project class FooProject(Project): def _build_foo(self): cwd = os.path.abspath('foo') subprocess.run(['make'], cwd=cwd, capture_output=True, check=True) def build(self): self._build_foo() super().build() def build_sdist(self, sdist_directory): self._build_foo() return super().build_sdist(sdist_directory) def build_wheel(self, wheel_directory): self._build_foo() return super().build_wheel(wheel_directory) def install(self): self._build_foo() super().install()
Мы объявили класс FooProject, производный от класса sipbuild.Project и преопределили в нем методы build, build_sdist, build_wheel и install. Во всех этих методах мы вызываем одноименные методы из базового класса, вызвав перед этим метод _build_foo, который запускает выполнение команды make в папке foo.
Обратите внимание, что методы build_sdist и build_wheel должны вернуть имя созданного ими файла. Это не написано в документации, но указано в исходниках SIP.
Теперь нам не нужно запускать команду make вручную для сборки библиотеки foo, это будет сделано автоматически.
Если теперь в папке pyfoo_c_04 выполнить команду sip-wheel, то будет создан файл с именем pyfoo-0.4-cp38-cp38-manylinux1_x86_64.whl или аналогичный в зависимости от вашей операционной системы и версии Python.
Этот пакет можно установить с помощью команды:
pip install --user --upgrade pyfoo-0.4-cp38-cp38-manylinux1_x86_64.whl
После этого можно убедиться, что функция foo из модуля foo по-прежнему работает.
Добавляем параметры командной строки для сборки
Следующий пример содержится в папке pyfoo_c_05, а пакет имеет номер версии 0.5 (см. настройки в файле pyproject.toml). Этот пример создан на основе примера из документации с некоторыми исправлениями. В этом примере мы переделаем наш файл project.py и добавим новые параметры командной строки для сборки.
В наших примерах мы собираем очень простую библиотеку foo, а в реальных проектах библиотека может быть достаточно большой и тогда не будет смысла ее включать в исходники проекта Python-обвязки. Напомню, что SIP изначально создавался для создания обвязки для такого огромной библиотеки как Qt. Можно, конечно, возразить, что для организации исходников могут помочь подмодули из git, но не в этом суть. Предположим, что библиотека может находиться не в папке с исходниками обвязки. В этом случае возникает вопрос, где сборщик SIP должен искать заголовочные и объектные файлы библиотеки? В этом случае пути размещения библиотеки у разных пользователей могут быть свои.
Чтобы решить эту проблему, добавим два новых параметра командной строки в систему сборки, с помощью которых можно будет указывать путь до файла foo.h (параметр --foo-include-dir) и до объектного файла библиотеки (параметр --foo-library-dir). Кроме того будем подразумевать, что если эти параметры не указаны, то библиотека foo расположена по-прежнему вместе с исходниками обвязки.
Нам нужно снова создать файл project.py, а в нем объявить класс, производный от sipbuild.Project. Давайте сначала посмотрим на новую версию файла project.py, а потом разберемся, как он работает.
import os from sipbuild import Option, Project class FooProject(Project): """ Проект с дополнительными параметрами командной строки для задания путей до заголовочных и объектных файлов библиотеки foo. """ def get_options(self): """ Возвращает список опций командной строки. """ tools = ['build', 'install', 'sdist', 'wheel'] # Получить стандартные опции. options = super().get_options() # Добавить новые опции inc_dir_option = Option('foo_include_dir', help="the directory containing foo.h", metavar="DIR", default=os.path.abspath('foo'), tools=tools) options.append(inc_dir_option) lib_dir_option = Option('foo_library_dir', help="the directory containing the foo library", metavar="DIR", default=os.path.abspath('foo/bin'), tools=tools) options.append(lib_dir_option) return options def apply_user_defaults(self, tool): """ Применить настройки по умолчанию. """ # Применить стандартные настройки по умолчанию super().apply_user_defaults(tool) # Чтобы гарантировать, что пути до заголовочных файлов и собранной библиотеки абсолютные self.foo_include_dir = os.path.abspath(self.foo_include_dir) self.foo_library_dir = os.path.abspath(self.foo_library_dir) def update(self, tool): """ Обновить конфигурацию проекта. """ # Получить обвязки pyfoo # (в файле pyproject.toml раздел [tool.sip.bindings.pyfoo]) foo_bindings = self.bindings['pyfoo'] # Установим параметр include_dirs для обвязки if self.foo_include_dir is not None: foo_bindings.include_dirs = [self.foo_include_dir] # Установим параметр library_dirs для обвязки if self.foo_library_dir is not None: foo_bindings.library_dirs = [self.foo_library_dir] super().update(tool)
Мы снова создали класс FooProject, производный от sipbuild.Project. В этом примере отключена автоматическая сборка библиотеки foo, потому что теперь подразумевается, что она может находиться в каком-нибудь другом месте, и к моменту создания обвязки уже должны быть готовы заголовочные и объектные файлы.
В классе FooProject переопределены три метода: get_options, apply_user_defaults и update. Рассмотрим их более внимательно.
Начнем с метода get_options. Этот метод должен возвращать список экземпляров класса sipbuild.Option. Каждый элемент списка — это опция командной строки. Внутри переопределенного метода мы получаем список опций по умолчанию (переменная options) с помощью вызова одноименного метода базового класса, затем создаем две новые опции (--foo_include_dir и --foo_library_dir) и добавляем их в список, после чего возвращаем этот список из функции.
Конструктор класса Option принимает один обязательный параметр (имя опции) и достаточно большое количество необязательных, описывающие тип значения для этого параметра, значение по умолчанию, описание параметра и некоторые другие. В этом примере используются следующие параметры конструктора Option:
- help задает описание параметра, которое можно увидеть, если запустить команду вроде sip-wheel -h
- metavar — строковое значение, которое для пользователя описывает, что должно представлять собой значение данного параметра. В нашем примере параметр metavar равен «DIR», чтобы подсказать пользователю, что значение этого параметра — директория.
- default — значение по умолчанию для параметра. В нашем примере подразумевается, что если не указаны пути к заголовочным и объектным файлам, то библиотека foo расположена там же, где и в предыдущих примерах (в папке с исходниками обвязки).
- tools — список строк, описывающих к каким командам должна применяться данная опция. В нашем примере мы добавляем параметры к sip-build, sip-install, sip-sdist и sip-wheel, поэтому tools = ['build', 'install', 'sdist', 'wheel'].
Внутри метода apply_user_defaults делаем так, чтобы пути в переменных self.foo_include_dir и self.foo_library_dir всегда были абсолютными. Это нужно чтобы не зависеть от того, какой будет рабочая папка в момент запуска сборки.
Последний перегруженный метод в этом классе — update. Этот метод вызывается, когда нужно применить к проекту выполненные до этого изменения. Например, изменить или добавить параметры, заданные в файле pyproject.toml. В предыдущих примерах мы устанавливали пути до заголовочных и объектных файлов с помощью параметров include-dirs и library-dirs соответственно внутри раздела [tool.sip.bindings.pyfoo]. Теперь эти параметры мы будем устанавливать из скрипта project.py, поэтому в файле pyproject.toml эти параметры удалим:
[build-system] requires = ["sip >=5,
Внутри метода update мы из словаря self.bindings по ключу pyfoo достаем экземпляр класса sipbuild.Bindings. Имя ключа соответствует разделу [tool.sip.bindings.pyfoo] из файла pyproject.toml, и полученный таким образом экземпляр класса описывает настройки, описанные в этом разделе. Затем членам этого класса include_dirs и library_dirs (имена членов соответствуют параметрам include-dirs и library-dirs с заменой дефиса на нижнее подчеркивание) присваиваем списки, содержащие пути, хранящиеся в членах self.foo_include_dir и self.foo_library_dir. В этом примере для аккуратности производится проверка на то, что значения self.foo_include_dir и self.foo_library_dir не равны None, но в данном примере это условие всегда выполняется, потому что у созданных нами параметров командной строки есть значения по умолчанию.
Таким образом мы подготовили файлы настроек для того, чтобы при сборке можно было указывать пути до заголовочных и объектных файлов. Проверим, что получилось.
Для начала убедимся, что работают значения по умолчанию. Для этого нужно зайти в папку pyfoo_c_05/foo и собрать библиотеку с помощью команды make, поскольку мы отключили автоматическую сборку библиотеки в этом примере.
После этого заходим в папку pyfoo_c_05 и запускаем команду sip-wheel. В результате выполнения этой команды будет создан файл pyfoo-0.5-cp38-cp38-manylinux1_x86_64.whl или с похожим названием.
Теперь перенесем папку foo куда-нибудь за пределы папки pyfoo_c_05 и снова запустим команду sip-wheel. В результате получим ожидаемую ошибку, сообщающую, что у нас нет объектного файла библиотеки:
usr/bin/ld: невозможно найти -lfoo collect2: ошибка: выполнение ld завершилось с кодом возврата 1 sip-wheel: Unable to compile the 'foo' module: command 'g++' failed with exit status 1
После этого запустим sip-wheel с использованием новых параметром командной строки:
sip-wheel --foo-include-dir ". /foo" --foo-library-dir ". /foo/bin"
Вместо многоточия нужно указать путь до папки, куда вы перенесли папку foo с собранной библиотекой. В результате сборка должна закончиться успешно созданием файла .whl. Созданный модуль можно установить и протестировать так же, как это делали в предыдущих разделах.
Проверяем порядок вызова методов из project.py
Следующий пример, который мы рассмотрим, будет совсем простым, он продемонстрирует порядок вызова методов класса Project, которые мы перегружали в предыдущих разделах. Это может быть полезно для того, чтобы понять, когда можно инициализировать переменные. Данный пример находится в папке pyfoo_c_06 в репозитории с исходниками.
Суть этого примера состоит в том, чтобы в классе FooProject, который расположен в файле project.py, перегрузить все методы, которые мы использовали до этого, и добавить в них вызовы функции print, которая бы выводила имя метода, в котором она находится:
from sipbuild import Project class FooProject(Project): def get_options(self): print('get_options()') options = super().get_options() return options def apply_user_defaults(self, tool): print('apply_user_defaults()') super().apply_user_defaults(tool) def apply_nonuser_defaults(self, tool): print('apply_nonuser_defaults()') super().apply_nonuser_defaults(tool) def update(self, tool): print('update()') super().update(tool) def build(self): print('build()') super().build() def build_sdist(self, sdist_directory): print('build_sdist()') return super().build_sdist(sdist_directory) def build_wheel(self, wheel_directory): print('build_wheel()') return super().build_wheel(wheel_directory) def install(self): print('install()') super().install()
Внимательные читатели должны заметить, что помимо ранее использованных методов, в этом примере перегружен еще метод apply_nonuser_defaults(), о котором мы раньше не говорили. В этом методе рекомендуют устанавливать значения по умолчанию для всех переменных, которые нельзя изменить через параметры командной строки.
В файле pyproject.toml вернем явное указание пути до библиотеки:
[build-system] requires = ["sip >=5,
Чтобы проект успешно собрался, нужно войти в папку foo и собрать там библиотеку с помощью команды make. После этого вернуться в папку pyfoo_c_06 и запустить, например, команду sip-wheel. В результате, если отбросить предупреждения компилятора, будет выведен следующий текст:
get_options()
apply_nonuser_defaults()
get_options()
get_options()
apply_user_defaults()
get_options()
update()
These bindings will be built: pyfoo.
build_wheel()
Generating the pyfoo bindings…
Compiling the 'foo' module…
The wheel has been built.
Полужирным шрифтом выделены строки, которые выводятся из нашего файла project.py. Таким образом мы видим, что метод get_options вызывается несколько раз, и это надо учитывать, если вы собираетесь инициализировать какую-нибудь переменную-член в классе, производный от Project. Метод get_options для этого — не лучшее место.
Также полезно запомнить, что метод apply_nonuser_defaults вызывается до метода apply_user_defaults, т.е. в методе apply_user_defaults уже можно использовать переменные, значения которых установлены в методе apply_nonuser_defaults.
После этого вызывается метод update, а в самом конце метод, отвечающий непосредственно за сборку, в нашем случае — build_wheel.
Заключение к первой части
В этой статье мы начали изучать инструмент SIP, предназначенный для создания Python-обвязок (Python bindings) для библиотек, написанных на языках C или C++. В этой первой части статьи мы рассмотрели основы использования SIP на примере создания Python-обвязки для очень простой библиотеки, написанной на языке C.
Мы разобрались с файлами, которые необходимо создать для работы с SIP. В файле pyproject.toml содержится информация о пакете (название, номер версии, лицензия и пути до заголовочных и объектных файлов). С помощью файла project.py можно влиять на процесс сборки пакета Python, например, запускать сборку C-библиотеки или дать возможность пользователю указывать расположение заголовочных и объектных файлов библиотеки.
В файле *.sip описывается интерфейс Python-модуля с перечислением функций и классов, которые будут содержаться в модуле. Для описания интерфейса в файле *.sip используются директивы и аннотации.
Во второй части статьи мы создадим обвязку над объектно-ориентированной библиотекой, написанной на C++, и на ее примере изучим приемы, которые будут полезны при описании интерфейса классов C++, а заодно разберемся с новыми для нас директивами и аннотациями.
Ссылки
- Примеры для данной статьи
- Домашняя страница SIP
- Документация для SIP