····{# В следующей строке будет добавлено содержимое шаблона site.html #}
····{% block content %}{% endblock %}
К этому моменту вы уже установили Python, выбрали редактор, знаете значение слова «питонский», прочитали несколько строк отличного кода Python и можете поделиться собственным кодом с остальным миром. Эта часть руководства поможет вам выбрать библиотеки для вашего продукта независимо от того, что вы решите реализовать. Мы поделимся наиболее распространенными подходами сообщества к определенным сценариям, они сгруппированы по принципу подобия.
• Глава 7 «Взаимодействие с пользователем». Мы рассмотрим библиотеки для всех типов взаимодействия с пользователем — от консольных приложений до графических и веб-приложений.
• Глава 8 «Управление кодом и его улучшение». Мы опишем инструменты для системного администрирования, взаимодействия с библиотеками, написанными на C и C++, а также перечислим способы повысить скорость работы Python.
• Глава 9 «Программные интерфейсы». Мы рассмотрим библиотеки, используемые для работы с сетью, включая асинхронные, а также библиотеки для сериализации и шифрования.
• Глава 10 «Манипуляции с данными». Мы рассмотрим библиотеки, которые предоставляют символьные и численные алгоритмы, графики, а также инструменты для обработки изображений и аудио.
• Глава 11 «Хранение данных». Наконец, изучим отличия между популярными библиотеками для ORM.
Библиотеки, представленные в этой главе, помогают разработчикам писать код для взаимодействия с конечными пользователями. Мы опишем уникальный проект Jupyter и рассмотрим типичные интерфейсы для командной строки и GUI. Завершается глава рассмотрением инструментов для веб-приложений.
Jupyter (http://jupyter.org/) — это веб-приложение, которое позволяет вам отображать и интерактивно выполнять код Python. Представляет собой интерфейс между пользователями.
Пользователи видят интерфейс клиента Jupyter — написанный на CSS, HTML и JavaScript — в браузере на клиентской машине. Клиент связывается с ядром, написанным на Python (или одном из множества других языков), которое выполняет блоки кода и возвращает результат. Содержимое хранится на серверной машине в формате notebook (*.nb) — текст в формате JSON разбит на несколько «клеток», которые могут содержать HTML, Markdown (доступный для прочтения человеком язык разметки; похож на язык, использующийся на вики-страницах), простые заметки или исполняемый код. Сервер может быть локальным (запущен на ноутбуке пользователя) или удаленным, как примеры на сайте https://try.jupyter.org/.
Серверу Jupyter для работы нужна версия Python не ниже 3.3, также он совместим с Python 2.7. Поставляется с наиболее свежими версиями дистрибутивов Python (описаны в разделе «Коммерческие дистрибутивы Python» главы 2), например Canopy и Anaconda, поэтому для этих инструментов не нужно выполнять дополнительную установку, если вы можете компилировать и выполнять сборку кода C в вашей системе, как мы обсуждали в главе 2. Выполнив подготовку, вы можете установить Jupyter из командной строки с помощью pip:
$ pip install jupyter
Последние исследования, посвященные использованию Jupyter для обучения (http://bit.ly/jupyter-classroom), показали, что это эффективный и популярный способ создавать интерактивные лекции для учеников, незнакомых с программированием.
Приложения командной строки (также их называют консольными) — это компьютерные программы, предназначенные для использования из текстового интерфейса вроде оболочки (http://en.wikipedia.org/wiki/Shell_(computing)). Они могут быть простыми командами (вроде pep8 или virtualenv) или интерактивными (вроде pip install, pip uninstall или pip freeze) — все они имеют собственные параметры в дополнение к общим параметрам pip. Как правило, все они начинают работу из функции main(). Наш BDFL поделился мнением (http://bit.ly/python-main-functions) о том, что нужно, чтобы написать хорошее консольное приложение.
Мы используем пример вызова pip для перечисления компонентов, которые могут присутствовать в момент вызова приложения командной строки:
Команда — это имя вызываемого исполняемого файла.
Аргументы следуют за командой и не начинаются с дефиса. Их также называют параметрами и субкомандами.
Параметры начинаются либо с одного дефиса (для отдельных символов вроде — h), либо с двух (для слов вроде — help). Их также называют флагами или переключателями.
Библиотеки, перечисленные в табл. 7.1, предоставляют разные способы анализа аргументов, а также другие полезные инструменты для приложений командной строки.
Как правило, сначала вы должны использовать инструменты стандартной библиотеки Python. Добавлять другие библиотеки в проект можно только в том случае, если в стандартной библиотеке нет нужной вам функциональности.
В следующих разделах представлена более подробная информация о каждом инструменте, перечисленном в табл. 7.1.
Библиотека | Лицензия | Причины использовать |
---|---|---|
argparse | Лицензия PSF | Находится в стандартной библиотеке. Предоставляет стандартный способ анализа аргументов и параметров |
docopt | Лицензия MIT | Позволяет управлять форматом вспомогательного сообщения. Анализирует командную строку в соответствии с соглашениями, определенными в стандарте POSIX (http://bit.ly/utility-conventions) |
plac | BSD 3-clause | Автоматически генерирует вспомогательное сообщение на основе существующей сигнатуры функции. Анализирует аргументы командной строки в фоновом режиме, передавая их непосредственно вашей функции |
click | BSD 3-clause | Предоставляет декораторы для создания вспомогательного сообщения и анализатора (похож на plac). Позволяет объединять несколько субкоманд. Взаимодействует с другими надстройками Flask (click не зависит от Flask, но он изначально был написан для того, чтобы помочь пользователям вместе создавать инструменты для командной строки с помощью разных надстроек для Flask, при этом ничего не разрушая, поэтому уже используется в экосистеме Flask) |
clint | Лицензия Internet Software | Имеет такие возможности форматирования, как изменение цвета, добавление отступов и колоночное отображение текста. Позволяет выполнить проверку типов для данных, введенных интерактивно (например, с помощью регулярного выражения, а также для целого числа или пути). Дает прямой доступ к списку аргументов, предоставляя простые инструменты для фильтрации и группирования |
Consortium (ISC) | ||
cliff | Лицензия Apache 2.0 | Предоставляет структурированный фреймворк для крупных проектов Python, имеющих много субкоманд. Создает интерактивную среду для использования субкоманд без дополнительного кодирования |
Модуль argparse (заменяет устаревший optparse) применяется при анализе параметров командной строки. Интерфейс командной строки, предоставленный проектом HowDoI, использует argparse — вы можете обратиться к нему при создании собственного интерфейса командной строки.
Рассмотрим код генерации анализатора:
import argparse
#
#… пропускаем кучу кода…
#
def get_parser():
····parser = argparse.ArgumentParser(description='…truncated for brevity…')
····parser.add_argument('query', metavar='QUERY', type=str, nargs='*',
························help='the question to answer')
····parser.add_argument('-p','-pos',
························help='select answer in specified position (default: 1)',
························default=1, type=int)
····parser.add_argument('-a','-all', help='display the full text
························of the answer',
························action='store_true')
····parser.add_argument('-l','-link', help='display only the answer link',
························action='store_true')
····parser.add_argument('-c', '-color', help='enable colorized output',
························action='store_true')
····parser.add_argument('-n','-num-answers', help='number of answers
························to return',
························default=1, type=int)
····parser.add_argument('-C','-clear-cache', help='clear the cache',
························action='store_true')
····parser.add_argument('-v','-version',
························help='displays the current version of howdoi',
························action='store_true')
····return parser
Анализатор проверит командную строку и создаст словарь, в котором соотносятся все аргументы и значения. Конструкция action='store_true' показывает, что параметр является флагом. При наличии в командной строке он будет сохранен как True в словаре анализатора.
Основной принцип docopt (http://docopt.org/) заключается в том, что документация должна быть красивой и понятной. Библиотека предоставляет одну основную команду docopt.docopt(), а также несколько функций и классов для удобства продвинутых пользователей. Функция docopt.docopt() принимает инструкции в стиле POSIX, написанные разработчиком, использует их для интерпретации аргументов командной строки и возвращает словарь со всеми аргументами и параметрами, полученными из командной строки. Также она корректно обрабатывает параметры — help и — version.
В следующем примере значение переменной arguments — это словарь, который имеет ключ name, — capitalize и — num_repetitions:
#!/usr/bin env python3
"""Здоровается с вами.
··Использование:
····hello … [options]
····hello — h | — help | — version
····-c, — capitalize писать ли имя с большой буквы
····-n REPS, — num_repetitions=REPS количество повторений [по умолчанию: 1]
"""
__version__ = "1.0.0" # Необходимо для параметра — version
def hello(name, repetitions=1):
····for rep in range(repetitions):
········print('Hello {}'.format(name))
if __name__ == "__main__":
····from docopt import docopt
····arguments = docopt(__doc__, version=__version__)
····name = ' '.join(arguments[''])
····repetitions = arguments['-num_repetitions']
····if arguments['-capitalize']:
········name = name.upper()
····hello(name, repetitions=repetitions)
Начиная с версии 0.6.0 docopt может использоваться для создания сложных программ с субкомандами, которые ведут себя как команды git (https://git-scm.com/) или svn (https://subversion.apache.org/) (даже если субкоманды написаны на разных языках). Существует полный пример приложения (https://github.com/docopt/docopt/tree/master/examples/git), имитирующий реализацию команды git, который показывает, как это возможно.
Философия Plac (https://pypi.python.org/pypi/plac) заключается в том, что вся информация, необходимая для анализа вызова команды, находится в сигнатуре целевой функции. Библиотека легковесна (примерно 200 строк), оборачивает argparse (http://docs.python.org/2/library/argparse.html) из стандартной библиотеки и предоставляет одну основную команду plac.plac(), которая получает анализатор аргумента из сигнатуры функции, анализирует командную строку, а затем вызывает функцию.
Библиотека должна была называться анализатором аргументов командной строки (Command-Line Argument Parser, clap), имя оказалось занято, поэтому она называется Plac — clap, почти наоборот. Руководство по использованию не особо информативно, но посмотрите, как мало строк в этом примере:
# hello.py
def hello(name, capitalize=False, repetitions=1):
··"""Здоровается с вами."""
····if capitalize:
········name = name.upper()
····for rep in range(repetitions):
········print('Hello {}'.format(name))
if __name__ == "__main__":
····import plac
····plac.call(hello)
Руководство по использованию выглядит так:
$ python hello.py — help
usage: hello.py [-h] name [capitalize] [repetitions]
Says hello to you.
positional arguments:
··name
··capitalize [False]
···repetitions [1]
optional arguments:
··-h, — help show this help message and exit
Если хотите выполнить преобразование типов для какого-нибудь аргумента до того, как передадите его в функцию, используйте декоратор annotations:
import plac
@plac.annotations(
····name = plac.Annotation("the name to greet", type=str),
····capitalize = plac.Annotation("use allcaps", kind="flag", type=bool),
····repetitions = plac.Annotation("total repetitions", kind="option",
··················type=int)
def hello(name, capitalize=False, repetitions=1):
··"""Здоровается с вами."""
····if capitalize:
········name = name.upper()
····for rep in range(repetitions):
········print('Hello {}'.format(name))
Помимо этого, декоратор plac.Interpreter предоставляет легковесный способ создать очень быстрое интерактивное приложение для командной строки. Примеры содержатся в документации к интерактивному режиму plac по адресу https://github.com/kennethreitz-archive/plac/blob/master/doc/plac_adv.txt.
Основное предназначение Click (http://click.pocoo.org/) (расшифровывается как Command Line-Interface Creation Kit — набор для создания интерфейсов командной строки) — помочь разработчикам создать компонуемые интерфейсы командной строки, написав минимально возможное количество кода. Документация к Click подтверждает ее связь с docopt.
Функция Click — создавать компонуемые системы, функция docopt — вручную создавать самые красивые интерфейсы командной строки. Эти две цели конфликтуют друг с другом. Click мешает пользователям реализовывать некоторые шаблоны для того, чтобы сделать интерфейсы командной строки уникальными. Например, вы практически не можете переформатировать вспомогательные страницы.
Стандарты этой библиотеки способны удовлетворить почти все запросы разработчиков, но продвинутые пользователи могут изменить их. Как и в случае с Plac, она задействует декораторы, чтобы привязать определения анализатора к функциям, которые будут их использовать, переместив из самих функций управление аргументами командной строки.
Приложение hello.py при использовании Click выглядит так:
import click
@click.command()
@click.argument('name', type=str)
@click.option('-capitalize', is_flag=True)
@click.option('-repetitions', default=1,
··············help="Times to repeat the greeting.")
def hello(name, capitalize, repetitions):
····"""Здоровается, with capitalization и именем."""
····if capitalize:
········name = name.upper()
····for rep in range(repetitions):
········print('Hello {}'.format(name))
if __name__ == '__main__':
····hello()
Click анализирует описание из строк документации команды и создает вспомогательное сообщение с помощью пользовательского анализатора, унаследованного от устаревшего члена стандартной библиотеки optparse, который более совместим со стандартами POSIX, чем argparse[82].
Вспомогательное сообщение выглядит так:
$ python hello.py — help
Usage: hello.py [OPTIONS] NAME
··Say hello, with capitalization and a name.
Options:
··-capitalize
··-repetitions INTEGER Times to repeat the greeting.
··-help Show this message and exit.
Реальная ценность Click заключается в ее модульности — вы можете добавить внешнюю функцию группировки, а затем любую другую функцию, декорированную с помощью click, в ваш проект, и они станут субкомандами для этой команды верхнего уровня:
Декоратор group() создает команды верхнего уровня, которые запускаются первыми (перед вызванной субкомандой).
С помощью декоратора pass_context (опционально) передаются объекты из сгруппированной команды в субкоманду, первый аргумент становится объектом click.core.Context.
Этот объект имеет специальный атрибут ctx.obj, который можно передавать субкомандам, использующим декоратор @click.pass_context.
Теперь вместо функции hello() вызывайте функцию, которая была декорирована @click.group(); в нашем случае это cli().
Библиотека Clint (https://pypi.python.org/pypi/clint/) соответствует своему названию и является набором инструментов для работы с интерфейсами командной строки (Command-Line INterface Tools). Поддерживает такую функциональность, как раскрашивание CLI и добавление отступов, индикаторов выполнения, основанных на итераторах, допускает неявную обработку аргументов. Кроме того, это простое и мощное средство отображения столбцов. В этом примере показаны инструменты для раскрашивания и создания отступов:
Использование indent интуитивно для менеджера контекста в операторе with. Флаг quote добавляет в начало каждой строки полужирную фиолетовую конструкцию ~*~.
Модуль colored имеет восемь функций для цветов, а также параметр отключения раскрашивания.
Функция puts() аналогична print(), обрабатывает отступы и кавычки.
Args предоставляет простые инструменты фильтрации для списка аргументов. Возвращает еще один объект Args, что позволяет объединять фильтры в цепочки.
Использовать аргументы args, созданные функцией Args(), можно именно так.
cliff (https://pypi.python.org/pypi/cliff) (Command-Line Interface Formulation Framework — фреймворк для формулирования интерфейсов командной строки) — это фреймворк для создания программ командной строки. Предполагается, что он должен использоваться для создания уникальных мультиуровневых команд, которые ведут себя как svn (Subversion) либо git, или интерактивных программ вроде оболочек Cassandra или SQL.
Функциональность cliff сгруппирована в абстрактных базовых классах. Вам нужно реализовать cliff.command.Command для каждой субкоманды, а затем cliff.commandmanager.CommandManager делегирует правильной команде. Рассмотрим минимальную версию программы hello.py:
cliff использует непосредственно argparse.ArgumentParser для интерфейса командной строки.
Получаем версию из setup.py (при последнем запуске pip install).
Для абстрактного базового класса требуется get_parser() — он должен возвращать argparse.ArgumentParser.
Для абстрактного базового класса требуется take_action() — он запускается при вызове команды Hello.
В основном приложении создается подкласс cliff.app.App, отвечающий за настройку журналирования, потоки ввода/вывода и все остальное, что можно применить ко всем субкомандам.
CommandManager управляет всеми классами Command, использует содержимое из entry_points файла setup.py для поиска имен команд.
В этом разделе мы сначала перечислим библиотеки с виджетами — наборы инструментов и фреймворки, которые предоставляют кнопки, полосы прокрутки, индикаторы выполнения и другие заранее созданные компоненты. В конце раздела коснемся игровых библиотек.
С точки зрения разработки графических интерфейсов, виджетами являются кнопки, слайдеры, полосы прокрутки и другие распространенные элементы интерфейса. С их помощью вам не нужно заниматься низкоуровневым программированием вроде определения того, какая кнопка (если таковая вообще существует) находилась под курсором мыши в момент клика, или работой с разными оконными API, используемыми разными операционными системами.
Если вы никогда не занимались разработкой интерфейсов, то вам нужно что-то, что легко использовать (чтобы вы поняли, как создавать интерфейсы). Мы рекомендуем Tkinter (находится в стандартной библиотеке Python). Вас заинтересуют структура и функции набора инструментов, который лежит в основе библиотеки, поэтому мы сгруппируем библиотеки по тулкитам, начав с самых популярных (табл. 7.2).
Библиотека, лежащая в основе (язык) | Библиотека Python | Лицензия | Причины использовать |
---|---|---|---|
Tk (Tcl) | tkinter | Лицензия Python Software Foundation | Все зависимости поставляются вместе с Python. Предоставляет стандартные виджеты для интерфейса вроде кнопок, полос прокрутки, текстовых окон и полотен |
SDL2 (C) | Kivy | MIT или LGPL3 (до версии 1.7.2) | Может использоваться для создания приложения для Android. |
Имеет функциональность для работы с технологией мультитач. Оптимизирована для С там, где это возможно, и использует GPU | |||
Qt (C++) | PyQt | GNU General Public License (GPL) или Commercial | На всех платформах выглядит одинаково. Многие приложения и библиотеки уже полагаются на Qt (например, Eric IDE, Spyder и/или Matplotlib), поэтому библиотека может оказаться установленной заранее. Qt5 (нельзя использовать вместе с Qt4) предоставляет инструменты для создания приложения для Android |
Qt (C++) | PySide | GNU Lesser General Public License (LGPL) | Полноценная замена для PyQt, имеющая более либеральную лицензию |
GTK (C) (тулкит GIMP) | PyGObject (PyGi) | GNU Lesser General Public License (LGPL) | Предоставляет связывание с Python для GTK+ 3. Должна быть знакома всем, кто уже разрабатывал для GNOME |
GTK (C) | PyGTK | GNU Lesser General Public License (LGPL) | Применяйте только в том случае, если ваш проект уже использует PyGTK; вы должны портировать старый код PyGTK к PyGObject |
wxWindows (C++) | wxPython | Лицензия wxWindows (модифицированная LGPL) | Предоставляет нативный внешний вид, предлагая различные оконные библиотеки для каждой платформы. Для разных платформ некоторые фрагменты кода будут отличаться |
Objective C | PyObjC | Лицензия MIT | Предоставляет интерфейс для работы с Objective C. Придаст вашему проекту для OS X нативный вид. Не может использоваться на других платформах |
В следующих разделах представлена более подробная информация о разных библиотеках для создания графического интерфейса для Python, они сгруппированы по лежащему в их основе тулкиту.
Tk Модуль стандартной библиотеки Tkinter — это тонкий объектно-ориентированный слой, покрывающий Tk, библиотеку виджетов, написанную на языке Tcl. (Вместе они выглядят как Tcl/Tk[83].) Поскольку модуль находится в стандартной библиотеке, он является наиболее удобным и совместимым GUI-тулкитом в нашем списке. Tk и Tkinter доступны на большинстве платформ Unix, а также в Windows и OS X.
На ресурсе TkDocs имеется хорошее руководство по Tk на нескольких языках, содержащее примеры на Python; более подробная информация представлена по адресу http://wiki.python.org/moin/TkInter.
Если у вас есть стандартный дистрибутив Python, у вас должен быть IDLE, графическая интерактивная среда для программирования, написанная исключительно на Python. Она является частью стандартной библиотеки — вы можете запустить ее из командной строки, введя команду idle, или просмотреть ее исходный код. Вы можете найти место, где она установлена, введя следующую команду в оболочке:
$ python — c" import idlelib; print(idlelib.__path__[0])"
В нашем каталоге много файлов, основное приложение IDLE запускается из модуля PyShell.py.
Аналогично для того, чтобы просмотреть пример использования интерфейса для рисования, tkinter.Canvas, взгляните на код модуля turtle. Вы можете найти его, введя следующую команду в оболочке:
$ python — c" import turtle; print(turtle.__file__)"
Kivy (http://kivy.org/) — это библиотека Python, предназначенная для разработки мультимедиа приложений с поддержкой технологии мультитач. Kivy активно разрабатывается сообществом, имеет разрешительную лицензию в стиле BSD и работает на всех крупных платформах (Linux, OS X, Windows и Android).
Kivy написан на Python и не использует тулкит для работы с окнами, взаимодействует непосредственно с SDL2 (Simple DirectMedia Layer) (https://www.libsdl.org/), библиотекой, написанной на C, которая предоставляет низкоуровневый доступ к устройствам ввода пользователя[84] и аудио, а также имеет доступ к отрисовке 3D-изображений с помощью OpenGL (или Direct3D для Windows). Имеет несколько виджетов (находятся в модуле kivy.uix — https://kivy.org/docs/api-kivy.uix.html), но их не так много, как в наиболее популярных альтернативах вроде Qt и GTK. Если вы разрабатываете традиционное десктопное приложение для бизнеса, больше подойдут Qt или GTK.
Для того чтобы установить библиотеку, перейдите на страницу загрузки Kivy https://kivy.org/#download, найдите свою операционную систему, загрузите ZIP-файл для своей версии Python и следуйте инструкциям для вашей ОС. Помимо кода поставляется и каталог с десятком примеров, которые показывают разные части API.
Qt Qt (произносится «кьют») (http://qt-project.org/) — это кросс-платформенный фреймворк, который широко применяется для разработки графического ПО, но его можно использовать и для разработки приложений без графики. Существует версия Qt5 для Android (http://doc.qt.io/qt-5/android-support.html). Если у вас уже установлен Qt (если вы работаете с Spyder, Eric IDE, Matplotlib или с другим инструментом, использующим Qt), вы можете узнать свою версию Qt из командной строки, введя следующую команду:
$ qmake — v
Qt выпущен под лицензией LGPL, что позволяет распространять бинарные файлы, которые работают с Qt, если вы не изменяете сам Qt. Коммерческая лицензия позволит воспользоваться такими инструментами, как визуализация данных и покупки в приложении. Qt предоставляет заранее созданные временные платформы для разных типов приложений. Оба интерфейса Python для Qt, PyQt и PySide, не очень хорошо задокументированы, поэтому лучший вариант — воспользоваться документацией к C++ от Qt (http://doc.qt.io/). Кратко опишем каждый интерфейс.
• PyQt от компании Riverbank Computing более актуален, нежели PySide (для которого нет версии для Qt5). Для того чтобы установить его, следуйте документации по установке PyQt4 или PyQt5 (http://pyqt.sourceforge.net/Docs/PyQt4/installation.html). PyQt4 работает только с Qt4, а PyQt5 — только с Qt5. (Мы предлагаем воспользоваться Docker — инструментом изоляции, который мы рассматривали в подразделе «Docker» раздела «Инструменты изоляции» главы 3, если вам действительно нужно разрабатывать код с помощью сразу обеих версий, чтобы постоянно не изменять пути к библиотекам.)
Компания Riverbank Computing также публикует pyqtdeploy (https://pypi.python.org/pypi/pyqtdeploy) — графический инструмент, работающий только с PyQt5, который генерирует код С++, характерный для платформы; его вы можете использовать для сборки бинарных файлов дистрибутива. Для получения более подробной информации обратитесь к руководству к PyQt4 (https://pythonspot.com/en/pyqt4/) и к примерам для PyQt5 (https://github.com/baoboa/pyqt5/tree/master/examples).
• PySide (https://wiki.qt.io/PySideDocumentation) был выпущен еще тогда, когда Qt принадлежала компании Nokia, поскольку они не могли заставить компанию Riverside Computing, создателей PyQt, изменить лицензию их продукта с GPL на LGPL. Интерфейс предполагается как полноценная замена для PyQt, но несколько замедляет PyQt при разработке. По адресу http://bit.ly/differences-pyside-pyqt описываются различия между PySide и PyQt.
Для того чтобы установить PySide, следуйте инструкциям из документации к Qt (https://wiki.qt.io/Setting_up_PySide). Существует также страница https://wiki.qt.io/Hello-World-in-PySide, которая поможет вам написать первое приложение с помощью PySide.
Библиотека виджетов GTK+ (http://www.gtk.org/) (расшифровывается как GIMP Toolkit — тулкит для GIMP)[85] предоставляет API основной среды рабочего стола GNOME. Программисты могут выбрать GTK+ вместо Qt в том случае, если они предпочитают C и им удобнее смотреть исходный код GTK+ или если они уже разрабатывали приложения GNOME ранее и знакомы с API. Рассмотрим две библиотеки, которые связывают Python и GTK+.
• pyGTK предоставляет связывание Python для GTK+, но в данный момент поддерживает только API для GTK+ 2.x (но не для GTK+ 3+). Она больше не поддерживается, и команда разработчиков рекомендует не использовать PyGTK для новых проектов и портировать старые проекты с PyGTK на PyGObject.
• PyGObject (известна как PyGI) (https://wiki.gnome.org/Projects/PyGObject) предоставляет связывание с Python, которое позволяет получить доступ ко всей программной платформе GNOME. Библиотека известна как PyGI, поскольку использует (и предоставляет Python API (http://lazka.github.io/pgi-docs/)) для исследования GObject (https://wiki.gnome.org/Projects/GObjectIntrospection), который является API-мостом между другими языками и основными библиотеками GNOME, написанными на C, а также для GLib (https://developer.gnome.org/glib/), если разработчики следуют соглашениям по определению GObject (https://developer.gnome.org/gobject/stable/pt02.html). Библиотека полностью совместима с GTK+ 3. Руководство Python GTK+ 3 Tutorial (http://python-gtk-3-tutorial.readthedocs.org/en/latest/) поможет начать работу с библиотекой.
Для того чтобы установить библиотеку, получите бинарные файлы с сайта загрузки PyGObject (http://bit.ly/pygobject-download) или, если пользуетесь OS X, установите ее с помощью homebrew, введя команду brew install pygobject.
Философия, лежащая в основе проекта wxWidgets (https://www.wxwidgets.org/), заключается в том, что лучший способ придать приложению нативный внешний вид — применить нативное API для каждой операционной системы. Qt и GTK+ теперь также используют другие оконные библиотеки помимо X11 за кулисами, но в Qt они абстрактны, а GTK придает им такой внешний вид, будто вы программируете GNOME. Преимущество wXWidgets заключается в том, что вы непосредственно взаимодействуете с каждой платформой, а лицензия позволяет вам даже больше. Проблема, однако, в том, что вам придется работать с каждой платформой по-разному.
Модуль расширения, который оборачивает wxWidgets для пользователей Python, называется wxPython (http://wxpython.org/). Когда-то он был самой популярной оконной библиотекой в Python, возможно, благодаря своей философии использования нативных инструментов для работы с интерфейсом, но теперь обходные решения в Qt и GTK+ стали достаточно удобными. Чтобы установить модуль, перейдите на сайт http://www.wxpython.org/download.php#stable и загрузите соответствующий пакет для вашей ОС. Начните знакомство с руководства для wxPython (http://bit.ly/wxpython-getting-started).
Objective-C — это проприетарный язык, используемый компанией Apple для операционных систем OS X и iOS, также вы можете получить доступ к фреймворку Cocoa для разработки приложений в OS X. В отличие от других вариантов, Objective-C не является кросс-платформенным; он предназначен для продуктов Apple.
PyObjC — это двухсторонний мост между языком Objective-C для OS X и Python, это означает, что не только у Python будет доступ к фреймворку Cocoa для разработки приложений в OS X, но и у программистов Objective-C появится доступ к Python[86].
Фреймворк Cocoa доступен только для OS X, поэтому не выбирайте Objective-C (через PyObjC), если вы пишете кросс-платформенное приложение.
Вам понадобится установить Xcode, как описано в разделе «Установка Python на Mac OS X» в главе 2, поскольку для PyObjC нужен компилятор. PyObjC работает только со стандартным дистрибутивом CPython (но не с дистрибутивами вроде PyPy или Jython), и мы рекомендуем использовать исполняемый файл Python, предоставленный OS X, поскольку эта версия Python была модифицирована компанией Apple и сконфигурирована специально для работы под OS X.
Для того чтобы указать вашей виртуальной среде использовать интерпретатор для Python из вашей системы, применяйте полный путь при его вызове. Если не хотите работать от лица суперпользователя, установите его с помощью переключателя — user (это действие сохранит библиотеку в каталоге $HOME/Library/Python/2.7/lib/python/site-packages/):
$ /usr/bin/python — m pip install — upgrade — user virtualenv
Активизируйте среду, войдите в нее и установите PyObjC:
$ /usr/bin/python — m virtualenv venv
$ source venv/bin/activate
(venv)$ pip install pyobjc
Для этого потребуется какое-то время. PyObjC поставляется вместе с py2app (рассматривается в подразделе «py2app» раздела «Замораживаем код» главы 6), который является инструментом для OS X, позволяющим создавать распространяемые отдельные бинарные файлы приложений. На странице примеров PyObjC (http://pythonhosted.org/pyobjc/examples/index.html) можно найти готовые приложения.
Kivy очень быстро стал популярным, но он имеет гораздо более крупный отпечаток, нежели библиотеки, перечисленные в этом разделе. Он был указан как фреймворк, поскольку предоставляет виджеты и кнопки, но его часто используют для создания игр. Сообщество Pygame ведет сайт для разработчиков (http://www.pygame.org/hifi.html), применяющих Python, на котором рады всем разработчикам независимо от того, используют они Pygame или нет. Рассмотрим наиболее популярные библиотеки, применяемые для разработки игр.
• cocos2d (https://pypi.python.org/pypi/cocos2d). Выпущена под лицензией BSD. Создана на основе pyglet, предоставляя фреймворк для структурирования игры в виде набора сцен, связанных пользовательскими workflows, работой управляет режиссер. Используйте ее, если вам нравится стиль «сцена-режиссер-workflow», который описан в документации по адресу http://tinyurl.com/py-cocos2d-scenes, или если хотите задействовать pyglet для рисования и SDL2 для джойстика и аудио. Вы можете установить cocos2D с помощью pip. Что касается SDL2, сначала проверьте его наличие в вашем менеджере пакетов, а затем загрузите с сайта https://www.libsdl.org/. Лучший способ начать работу — изучить поставляемые примеры приложений для cocos2d (https://github.com/los-cocos/cocos/tree/master/samples).
• pyglet (https://pypi.python.org/pypi/pyglet). Выпущена под лицензией BSD. Представляет собой набор легковесных оболочек для OpenGL, а также инструменты для представления и перемещения спрайтов по окну. Просто установите ее — вам должно хватить pip, поскольку практически на каждом компьютере имеется OpenGL — и запустите несколько примеров приложений (https://bitbucket.org/pyglet/pyglet/src/default/examples), которые включают в себя полноценный клон игры Asteroids (http://bit.ly/astrea-py) (для его написания потребовалось менее чем 800 строк кода).
• Pygame. Выпущена под лицензией Zlib, а также под лицензией GNU LGPLv2.1 для SDL2. Существует крупное активное сообщество, вступив в которое, вы можете получить множество руководств по Pygame (http://www.pygame.org/wiki/tutorials), но члены этого сообщества используют SDL1, предыдущую версию библиотеки. Она недоступна в PyPI, поэтому сначала поищите ее в своем менеджере пакетов, а затем, если ее нет, загрузите Pygame (http://www.pygame.org/download.shtml).
• Pygame-SDL2 (http://bit.ly/pygame-sdl2) была недавно объявлена как попытка заново реализовать Pygame с бэкендом в виде SDL2. Выпущена под теми же лицензиями, что и Pygame.
• PySDL2 (https://pypi.python.org/pypi/PySDL2) работает на CPython, IronPython и PyPy, является тонким интерфейсом к библиотеке SDL2. Если вам нужен самый маленький интерфейс между SDL2 и Python, эта библиотека подойдет идеально. Для получения более подробной информации смотрите руководство к PySDL2 (http://pysdl2.readthedocs.io/en/latest/tutorial/index.html).
Поскольку мощный язык сценариев был адаптирован как для быстрого прототипирования, так и для больших проектов, Python широко используется в разработке веб-приложений (на Python написаны YouTube, Pinterest, Dropbox и The Onion).
Две библиотеки, которые мы рассмотрели в главе 5 — Werkzeug и Flask, — были связаны со сборкой веб-приложений. С их помощью мы кратко описали интерфейс Web Server Gateway Interface (WSGI), стандарт Python, определенный в PEP 3333, который указывает, как общаются веб-серверы и веб-приложения, написанные на Python.
В этом разделе мы рассмотрим веб-фреймворки, написанные на Python, их систему шаблонов, серверы, с которыми они взаимодействуют, а также платформы, на которых они запускаются.
Веб-фреймворк состоит из набора библиотек и основного обработчика, внутри которого вы можете строить собственный код для реализации веб-приложения (то есть интерактивного сайта, предоставляющего его клиенту интерфейс к коду, запущенному на сервере). Большая часть веб-фреймворков включает в себя шаблоны и вспомогательные программы, позволяющие выполнить как минимум следующий действия.
• Маршрутизация URL. Соотнесение входящего запроса HTTP с соответствующей функцией Python (или вызываемой функцией).
• Обработка объектов Request и Response. Инкапсуляция информации, полученной или отправленной браузеру пользователя.
• Шаблоны. Внедрение переменных Python в шаблоны HTML или другую форму вывода информации, что позволяет программистам отделить логику приложения (в Python) от макета (в шаблоне).
• Веб-сервис для отладки. Запуск миниатюрного сервера HTTP на машинах разработчиков, позволяющего ускорить разработку; зачастую код на серверной стороне автоматически перезагружается при обновлении файлов.
Вам не нужно писать код вокруг фреймворка. Он изначально должен предоставлять все, что вам требуется, при этом функциональность должна быть протестирована другими разработчиками, поэтому, если вы все еще не можете найти то, что вам нужно, продолжайте исследовать другие доступные фреймворки (например, Bottle, Web2Py, CherryPy). Технический редактор также отметил, что мы должны упомянуть Falcon (http://falconframework.org/) — фреймворк, предназначенный для сборки RESTful API (но не для работы с HTML).
Все библиотеки, перечисленные в табл. 7.3, могут быть установлены с помощью команды pip:
$ pip install Django
$ pip install Flask
$ pip install tornado
$ pip install pyramid
Библиотека Python | Лицензия | Причины использовать |
---|---|---|
Django | Лицензия BSD | Предоставляет структуру — в основном заранее созданный сайт, где вы разрабатываете макет и данные/логику. Автоматически генерирует административный веб-интерфейс, где непрограммисты могут добавлять или удалять данные (например, новостные статьи). |
Поставляется вместе с инструментом для объектно-реляционного отображения (object-relational mapping, ORM) | ||
Flask | Лицензия BSD | Позволяет полностью контролировать все, что находится у вас в стеке. Предоставляет элегантные декораторы, которые добавляют маршрутизацию URL для любой выбранной вами функции. Позволяет вам отказаться от структуры, предоставленной Django или Pyramid |
Tornado | Лицензия Apache 2.0 | Предоставляет отличную функциональность по обработке событий — Tornado использует собственный сервер HTTP. Предоставляет способ обработать множество WebSockets (полнодуплексная, устойчивая коммуникация с помощью TCP*) или других долгоиграющих соединений сразу после установки |
Pyramid | Модифицированная лицензия BSD | Предоставляет заранее собранную структуру, которая называется временной платформой, но в меньшей степени, чем Django, позволяет использовать любой интерфейс базы данных или библиотеку шаблонов. Основан на популярном фреймворке Zope и на Pylons |
* Transmission Control Protocol (TCP) — стандартный протокол, который определяет способ, с помощью которого два компьютера устанавливают соединение друг с другом.
В следующих разделах приведена более подробная информация о веб-фреймворках из табл. 7.3.
Django (http://www.djangoproject.com/) — это готовый к работе веб-фреймворк. Отличный выбор для тех, кто создает сайты, ориентированные на содержимое. Предоставляя множество вспомогательных программ и шаблонов сразу после установки, Django позволяет создавать сложные веб-приложения, работающие с базами данных.
У Django имеется крупное и активное сообщество. Многие их модули, которые можно использовать повторно (http://djangopackages.com/), вы можете встроить в свой проект или настроить так, как вам нужно.
Проводятся ежегодные конференции в Соединенных Штатах (http://djangocon.us/) и Европе (http://djangocon.eu/), посвященные Django, — большая часть веб-приложений Python сегодня создается с помощью Django.
Flask (http://flask.pocoo.org/) — это микрофреймворк для Python. Отлично подойдет для сборки небольших приложений, API и веб-сервисов. Вместо предоставления всех инструментов, которые теоретически могут понадобиться, во Flask реализованы самые распространенные компоненты фреймворка для создания веб-приложений вроде маршрутизации URL, объекты запросов и ответов HTTP, а также шаблоны. Создание приложения с помощью Flask похоже на написание стандартного модуля Python, только к некоторым функциям прикреплены маршруты (с применением декоратора, как это показано в следующем фрагменте кода). Выглядит красиво:
@app.route('/deep-thought')
def answer_the_question():
return 'The answer is 42.'
Если вы используете Flask, ответственность за выбор других компонентов приложения (если таковые нужны) ложится на вас. Например, доступ к базам данных или генерация/проверка форм не встроены во Flask (поскольку многим веб-разработчикам эта функциональность не нужна). Если вам это требуется, существует множество доступных расширений (http://flask.pocoo.org/extensions/) вроде SQLAlchemy (http://flask-sqlalchemy.pocoo.org/) для баз данных, pyMongo (https://docs.mongodb.org/getting-started/python/) для MongoDB и WTForms (https://flask-wtf.readthedocs.org/) для работы с формами.
Flask является выбором по умолчанию для любого веб-приложения, которое не подходит для заранее созданных временных платформ от Django. Попробуйте запустить эти примеры приложений для Flask (https://github.com/pallets/flask/tree/master/examples), чтобы ознакомиться с фреймворком. Если хотите запустить несколько приложений (по умолчанию для Django), применяйте инструмент для планирования (http://bit.ly/application-dispatching). Если желаете дублировать наборы подстраниц внутри приложения, используйте Flask’s Blueprints («Эскизы Flask») (http://flask.pocoo.org/docs/0.10/blueprints/).
Tornado (http://www.tornadoweb.org/) — это асинхронный (управляемый событиями и неблокирующий, как Node.js (https://nodejs.org/en/)) веб-фреймворк для Python, который имеет собственный цикл событий[87]. Это позволяет нативно поддерживать, например, протокол коммуникации WebSockets (http://bit.ly/websockets-api). В отличие от других фреймворков, показанных в этом разделе, Tornado не является приложением WSGI (http://www.tornadoweb.org/en/stable/wsgi.html). Его можно запустить как приложение или сервер WSGI с помощью модуля tornado.wsgi, но даже его авторы спросят: «А в чем смысл?»[88], поскольку WSGI — это синхронный интерфейс, а Tornado — асинхронный фреймворк.
Tornado сложнее, чем Django или Flask, и используется гораздо реже. Работайте с ним только в том случае, если прирост производительности, связанный с применением асинхронного фреймворка, будет стоить дополнительного времени, затраченного на программирование. Если решитесь, хорошей стартовой точкой станут демонстрационные приложения (https://github.com/tornadoweb/tornado/tree/master/demos). Качественно написанные приложения Tornado славятся отличной производительностью.
Pyramid (http://www.pylonsproject.org/) похож на Django, однако больший упор в нем делается на модульность. Поставляется вместе с несколькими встроенными библиотеками (меньшее количество функциональности доступно сразу) и поощряет расширение его базовой функциональности с помощью шаблонов, которые называются временными платформами.
Вы регистрируете временную платформу, а затем вызываете ее при создании нового проекта с помощью команды pcreate — аналогично команде Django django-admin startproject project-name command, но у вас имеются параметры для работы с разными структурами, бэкендами для баз данных и маршрутизацией URL.
Pyramid не очень востребован у пользователей, в отличие от Django и Flask. Это хороший фреймворк, но он не считается популярным выбором для создания новых веб-приложений на Python.
По адресу http://docs.pylonsproject.org/projects/pyramid-tutorials можно найти несколько руководств для Pyramid. Чтобы убедить вашего босса использовать Pyramid, взгляните на портал https://trypyramid.com/, где имеется большой объем информации о Pyramid.
Большая часть приложений WSGI предназначена для реагирования на запросы HTTP и обслуживания содержимого в формате HTML или других языках разметки. Движки шаблонов отвечают за отрисовку этого содержимого: управляют набором файлов шаблонов, имеют систему иерархии и включения, позволяющую избежать лишнего повторения, и заполняют статическое содержимое шаблонов динамическим содержимым, сгенерированным приложением. Это помогает придерживаться концепции разделения обязанностей[89] — мы размещаем логику в коде, а отрисовку доверяем шаблонам.
Файлы шаблонов иногда пишутся дизайнерами или фронтенд-разработчиками, и сложность страниц может усложнить координирование. Рассмотрим правила хорошего тона как для приложений, передающих динамическое содержимое движку, так и для самих шаблонов.
• Никогда зачастую лучше, чем прямо сейчас. Файлам шаблонов должно передаваться только то динамическое содержимое, которое нужно отрисовать. Постарайтесь не передавать дополнительное содержимое на всякий случай: гораздо проще добавить отсутствующие переменные, чем убрать переменную, которая, скорее всего, не будет использоваться в работе.
• Постарайтесь держать логику за пределами шаблона. Многие движки шаблонов позволяют создавать сложные выражения или операции присваивания в самом шаблоне, а также встраивать код Python, который будет оценен именно в шаблоне. Это может привести к неконтролируемому росту сложности и зачастую усложняет поиск ошибок. Мы не против такого подхода — практичность побеждает красоту, — просто держите себя в руках.
• Отделяйте JavaScript от HTML. Зачастую необходимо смешивать шаблоны JavaScript с шаблонами HTML. Не теряйте голову: изолируйте те части, где шаблон HTML передает переменные в код JavaScript.
Все движки шаблонов, перечисленные в табл. 7.4, принадлежат ко второму поколению, их скорость отрисовки высока[90], а функциональность создана благодаря опыту работы со старыми библиотеками шаблонов.
Библиотека Python | Лицензия | Причины использовать |
---|---|---|
Jinja2 | Лицензия BSD | По умолчанию используется в Flask и поставляется с Django. Основана на языке шаблонов Django Template Language, в шаблоны можно добавить лишь немного логики. Jinja2 — это движок по умолчанию для Sphinx, Ansible и Salt (если вы использовали эти инструменты, вы знаете Jinja2) |
Chameleon | Модифицированная лицензия BSD | Шаблоны сами по себе являются корректными XML/HTML. Похожа на Template Attribute Language (TAL) и его derivatives |
Mako | Лицензия MIT | Используется по умолчанию в Pyramid. Разработана для повышения скорости выполнения программы — используйте, когда отрисовка шаблона ограничена во времени. Позволяет поместить большое количество кода в шаблоны — Mako похож на версию Python для PHP (http://php.net/) |
В следующих разделах приводится более подробная информация о библиотеках из табл. 7.4.
Мы рекомендуем выбирать Jinja2 (http://jinja.pocoo.org/) в качестве библиотеки шаблонов для новых веб-приложений Python. Используется в качестве движка по умолчанию в Flask и генераторе документации для Python Sphinx (http://www.sphinx-doc.org/), может применяться в Django, Pyramid и Tornado. Она работает с основанным на тексте языком шаблонов, поэтому подойдет для генерации любой разметки, а не только HTML.
Позволяет настраивать фильтры, теги, тесты и глобальные переменные. Предоставляет возможность добавлять логику в шаблоны, что сокращает объемы кода.
Рассмотрим важные теги Jinja2:
{# Это комментарий, он выделяется решеткой и фигурными скобками. #}
{# Так можно добавить переменную: #}
{{title}}
{# Так можно определить именованный блок, который можно заменить #}
{# на шаблон-потомок. #}
{% block head %}
This is the default heading.
{% endblock %}
{# Так можно выполнить итерирование: #}
{% for item in list %}
{{ item }}
{% endfor %}
Рассмотрим пример сайта в комбинации с веб-сервером Tornado, описанным в подразделе «Tornado» текущего раздела:
# импортируем Jinja2
from jinja2 import Environment, FileSystemLoader
# импортируем Tornado
import tornado.ioloop
import tornado.web
# Загружаем файл шаблона templates/site.html
TEMPLATE_FILE = "site.html"
templateLoader = FileSystemLoader(searchpath="templates/")
templateEnv = Environment(loader=templateLoader)
template = templateEnv.get_template(TEMPLATE_FILE)
# Список популярных фильмов
movie_list = [
····[1,"The Hitchhiker's Guide to the Galaxy"],
····[2,"Back to the Future"],
····[3,"The Matrix"]
]
# Метод template.render() возвращает строку, содержащую отрисованный HTML
html_output = template.render(list=movie_list, title="My favorite movies")
# Обработчик для основной страницы
class MainHandler(tornado.web.RequestHandler):
····def get(self):
····# Возвращает отрисованную строку шаблона запросу браузера
········self.write(html_output)
# Присваиваем обработчик на сервер (127.0.0.1:PORT/)
application = tornado.web.Application([
····(r"/", MainHandler),
])
PORT=8884
if __name__ == "__main__":
····# Настраиваем сервер
····application.listen(PORT)
····tornado.ioloop.IOLoop.instance(). start()
Файл base.html может быть использован в качестве основы для всех страниц сайта. В этом примере они могли бы быть реализованы в блоке content (в данный момент пуст):
····
····{{title}} — My Web Page
····{# В следующей строке будет добавлено содержимое шаблона site.html #}
····{% block content %}{% endblock %}
В следующем примере кода показана страница сайта (site.html), которая расширяет шаблон base.html. Блок content будет встроен автоматически в соответствующий блок base.html:
{% block content %}
····
····
········{{title}}
········{{ list_title }}
········
············{% for item in list %}
············{{ item[0]}}: {{ item[1]}}
············{% endfor %}
········
····
····
{% endblock %}
Chameleon Page Templates (файлы с расширением *.pt) (https://chameleon.readthedocs.org/) — это реализация в движке шаблонов синтаксисов Template Attribute Language (TAL) (http://en.wikipedia.org/wiki/Template_Attribute_Language), TAL Expression Syntax (TALES) (http://bit.ly/expression-tales) и Macro Expansion TAL (Metal) (http://bit.ly/macros-metal) для HTML/XML. Chameleon анализирует Page Templates и «компилирует» их в байт-код Python для повышения скорости загрузки.
Доступен в Python 2.5 и выше (включая 3.x и PyPy) и является одним из двух движков для отрисовки, используемых Pyramid по умолчанию (см. подробнее подраздел «Pyramid» ранее в этом разделе). (Вторым является Mako, описанный в следующем подразделе.)
Page Templates добавляет специальный элемент attributes и текстовую разметку для вашего XML-документа: набор простых языковых конструкций позволяет управлять потоком документов, повторением элементов, заменой текста и переводом. Благодаря синтаксису, основанному на атрибутах, неотрисованные шаблоны страниц представляют собой корректный HTML, могут быть просмотрены в браузере и даже отредактированы с помощью редакторов WYSIWYG (What you see is what you get — «Что видишь, то и получаешь»). Это позволяет упростить взаимодействие с дизайнерами, а также прототипирование с использованием статических файлов. Основы языка TAL легко освоить с помощью примера:
··
··Hello, World!
····
······
········
············
········
······
····
··
Шаблон для вставки текста достаточно распространен. Если вам не нужна стопроцентная корректность для неотрисованных шаблонов, вы можете заменить его на более сжатый и читаемый синтаксис с помощью шаблона ${expression}, как показано далее:
··
··Hello, ${world}!
····
······
········
············${row.capitalize()} ${col}
········
······
····
··
Но помните, что полный синтаксис Default Text также позволяет использовать содержимое по умолчанию в неотрисованном шаблоне.
Chameleon не очень популярен в местах, где уже пользуются Pyramid.
Mako (http://www.makotemplates.org/) — это язык шаблонов, который компилируется в Python для максимальной производительности. Его синтаксис и API заимствованы из других языков шаблонов вроде Django и Jinja2. Этот язык шаблонов по умолчанию включается в веб-фреймворке Pyramid (рассматривается в подразделе «Pyramid» текущего раздела). Пример шаблона Mako выглядит так:
<%inherit file="base.html"/>
<%
····rows = [[v for v in range(0,10)] for row in range(0,10)]
%>
····% for row in rows:
········${makerow(row)}
····% endfor
<%def name="makerow(row)">
····
····% for name in row:
········${name} \
····% endfor
····
%def>
Это текстовый язык разметки, как Jinja2, поэтому его можно использовать для чего угодно (не только для документов XML/HTML). Чтобы отрисовать простой шаблон, введите следующий код:
from mako.template import Template
print(Template("hello ${data}!"). render(data="world"))
Mako имеет хорошую репутацию в сообществе Python. Он быстр и позволяет разработчикам встраивать большое количество логики Python в страницу. Правда, мы рекомендовали бы делать это с осторожностью (только при большой необходимости).
Два варианта, которые мы рассмотрим в этом разделе, связаны либо с хостингом (то есть вы платите поставщику вроде Heroku, Gondor или PythonAnywhere, чтобы он управлял сервером и базой данных за вас), либо с настройкой собственной инфраструктуры на машине, предоставленной хостингом для виртуальных выделенных серверов (virtual private server, VPS) вроде Amazon Web Services (http://aws.amazon.com/) или Rackspace (https://www.rackspace.com/). Кратко рассмотрим оба варианта.
Платформа как услуга (Platform as a Service, PaaS) — это разновидность облачной вычислительной инфраструктуры, которая абстрагирует инфраструктуру и управляет ею (например, настраивает базу данных и веб-сервер, поддерживает их с помощью обновлений, связанных с безопасностью), а также отвечает за маршрутизацию и масштабирование приложений. При использовании PaaS разработчики приложений могут сфокусироваться на написании кода, а не на управлении развертыванием.
Существует множество поставщиков PaaS, конкурирующих друг с другом, но поставщики, представленные в этом списке, нацелены на сообщество Python. Большинство предлагают бесплатный тестовый период.
• Heroku (http://www.heroku.com/python). Рекомендуем использовать Heroku при развертывании веб-приложений Python. Он поддерживает приложения всех типов, написанные на Python 2.7–3.5: веб-приложения, серверы и фреймворки. Вам также предоставляется набор инструментов для командной строки (https://toolbelt.heroku.com/), чтобы работать с учетной записью Heroku, базой данных и веб-серверами, которые поддерживает ваше приложение, поэтому вы можете вносить изменения без веб-интерфейса. Heroku предлагает подробные статьи (https://devcenter.heroku.com/categories/python), посвященные использованию Python и Heroku, а также пошаговые инструкции (https://devcenter.heroku.com/articles/getting-started-with-python) о том, как настроить ваше первое приложение.
• Gondor (https://gondor.io/). Поддерживается небольшой компанией, концентрируется на том, чтобы помогать бизнесам добиваться успеха с помощью Python и Django. Его платформа предназначена для развертывания приложений Django и Pinax[91]. Платформой Gondor являются Ubuntu 12.04 и версии Django 1.4, 1.6, и 1.7, а также подмножество реализаций Python 2 и 3, перечисленных по адресу https://gondor.io/support/runtime-environment/. Он может автоматически сконфигурировать ваш сайт, написанный с помощью Django, если вы используете local_settings.py для размещения характерной для сайта информации о конфигурации. Для получения более подробных сведений обратитесь к руководству по Gondor (https://gondor.io/support/django/setup/); также доступен инструмент для командной строки (https://gondor.io/support/client/).
• PythonAnywhere (https://www.pythonanywhere.com/). Поддерживает Django, Flask, Tornado, Pyramid и множество других фреймворков для создания веб-приложений, которые мы не описывали, вроде Bottle (не фреймворк, как и Flask, но сообщество гораздо меньше) и web2py (отлично подходит для обучения).
Ценовая политика хостинга связана со временем, затрачиваемым на вычисления (вместо того чтобы брать большие суммы, хостинг прерывает вычисления, как только их объем превышает дневной максимум. Это удобно для разработчиков, следящих за стоимостью).
За исключением Tornado (поставляется с собственным сервером HTTP) все фреймворки для создания веб-приложений, которые мы рассмотрели, являются приложениями WSGI. Это значит, что они должны взаимодействовать с сервером WSGI так, как это указано в PEP 3333, для того чтобы получить запрос HTTP и отправить ответ HTTP.
Большая часть самостоятельно размещаемых приложений Python сегодня находится на сервере WSGI вроде Gunicorn (сервера WSGI зачастую можно использовать как отдельные сервера HTTP) или на базе легковесного веб-сервера вроде Nginx. Если задействуются оба варианта, серверы WSGI взаимодействуют с приложениями Python в то время, как веб-сервер обрабатывает более подходящие задачи — выдачу статических файлов, маршрутизацию запросов, защиту от распределенных атак типа отказа в обслуживании (distributed denial-of-service, DDoS) и базовую аутентификацию. Далее описываются два наиболее популярных веб-сервера — Nginx и Apache.
• Nginx (http://nginx.org/). Nginx (произносится «энжин-икс») — это веб-сервер и обратный прокси[92] для HTTP, SMTP и других протоколов. Отличается высокой производительностью, относительной простотой и совместимостью со многими серверами приложений (вроде серверов WSGI). Включает такие полезные функции, как балансировка нагрузки[93], базовая аутентификация, поточный режим и т. д. Nginx становится довольно популярным, поскольку разработан для обслуживания загруженных сайтов.
• Сервер HTTP Apache. Apache — это наиболее популярный сервер HTTP в мире (http://w3techs.com/technologies/overview/web_server/all), но мы предпочитаем Nginx. Однако те разработчики, кто только сейчас столкнулся с проблемой развертывания, могут захотеть начать с Apache и mod_wsgi (https://pypi.python.org/pypi/mod_wsgi), который считается самым простым интерфейсом WSGI. В документации к каждому фреймворку — mod_wsgi с Pyramid (http://bit.ly/pyramidwsgi), mod_wsgi с Django (http://bit.ly/django-mod_wsgi) и mod_wsgi и Flask (http://bit.ly/flask-mod_wsgi) — вы можете найти полезные руководства.
Отдельные серверы WSGI обычно используют меньше ресурсов, чем традиционные веб-серверы, и предоставляют лучшую производительность для серверов WSGI в Python (http://nichol.as/benchmark-of-python-web-servers). Их можно использовать вместе с Nginx или Apache, которые могут служить обратными прокси. Рассмотрим наиболее популярные серверы WSGI.
• Gunicorn (Green Unicorn) (http://gunicorn.org/). Мы рекомендуем использовать Gunicorn для новых веб-приложений — он написан на чистом Python и применяется для обслуживания приложений Python. В отличие от других веб-серверов Python, он имеет продуманный пользовательский интерфейс, и его весьма легко сконфигурировать и использовать. Gunicorn имеет адекватные и разумные стандартные значения для конфигурации. Однако некоторые другие серверы вроде uWSGI можно настроить гораздо тщательнее (но именно поэтому работать с ними сложнее).
• Waitress (http://waitress.readthedocs.org/). Это написанный на чистом Python сервер WSGI. Говорят, что он имеет «очень приемлемую производительность». Его документация не особо подробна, но он предлагает такую функциональность, которой не имеет Gunicorn (например, буферизацию запросов HTTP); он не блокирует запрос, когда клиент отвечает слишком медленно (отсюда и произошло его имя Wait-ress[94]). Waitress набирает популярность среди веб-разработчиков Python.
• uWSGI (https://uwsgi-docs.readthedocs.org/). Предоставляет всю функциональность, необходимую для сборки хостингового сервиса. Мы не рекомендуем использовать его как отдельный веб-маршрутизатор (если только вы не уверены в том, что он вам нужен).
uWSGI также может работать на базе полноценного веб-сервера (вроде Nginx или Apache). Веб-сервер может сконфигурировать uWSGI и работу приложения с помощью протокола uwsgi (http://bit.ly/uwsgi-protocol). Поддержка веб-сервера uWSGI позволяет динамически конфигурировать Python, передавать переменные среды и выполнять другие настройки. Для получения более подробной информации обратитесь к волшебным переменным uWSGI (http://bit.ly/uwsgimagicvar).
В этой главе рассматриваются библиотеки, которые позволяют управлять процессами разработки, интеграцией систем, сервером и оптимизацией производительности, а также упрощать код.
Никто не опишет процесс непрерывной интеграции лучше, чем Мартин Фаулер (Martin Fowler)[95].
Непрерывная интеграция — это практика разработки ПО, согласно которой члены команды часто объединяют свои наработки. Обычно каждый человек делает это как минимум ежедневно, что приводит к выполнению множества интеграций за день. Каждая интеграция проверяется путем автоматической сборки (включая тесты), чтобы максимально быстро обнаружить ошибки интеграции. Многие команды считают, что такой подход значительно снижает количество проблем, возникающих при интеграции, а также позволяет быстрее разрабатывать целостность ПО.
Тремя наиболее популярными инструментами для непрерывной интеграции в данный момент являются Travis-CI, Jenkins и Buildbot (перечислены в следующих разделах). Они часто используются с Tox, инструментом Python для управления virtualenv и тестами из командной строки. Travis помогает работать с несколькими интерпретаторами Python на одной платформе, а Jenkins (самый популярный) и Buildbot (написан на Python) могут управлять сборками на нескольких машинах. Многие также используют Buildout (рассмотрен в подразделе «Buildout» раздела «Инструменты изоляции» главы 3) и Docker (рассмотрен в подразделе «Docker» там же) для быстрой и частой сборки сложных сред для батареи тестов.
Tox. Tox (http://tox.readthedocs.org/en/latest/) — это инструмент автоматизации, предоставляющий функциональность упаковки, тестирования и развертывания ПО, написанного на Python, из консоли или сервера непрерывной интеграции. Является общим инструментом командной строки для управления и тестирования, который предоставляет следующие функции:
• проверка того, что все пакеты корректно устанавливаются для разных версий и интерпретаторов Python;
• запуск тестов в каждой среде, конфигурирование избранных инструментов тестирования;
• выступает в роли фронтенда для серверов непрерывной интеграции, снижая шаблонность и объединяя тесты для непрерывной интеграции и тесты для оболочки.
Вы можете установить Tox с помощью pip:
$ pip install tox
Инструменты, показанные в этом разделе, предназначены для наблюдения за системами и управления ими (автоматизация сервера, наблюдение за системами и управление потоком выполнения).
Travis-CI (https://travis-ci.org/) — распределенный сервер непрерывной интеграции, позволяющий создавать тесты для проектов с открытым исходным кодом бесплатно. Предоставляет несколько рабочих процессов, которые запускают тесты Python, и бесшовно интегрируется с GitHub. Вы даже можете указать ему оставлять комментарии для ваших запросов на включение[96], если этот конкретный набор изменений сломает сборку. Поэтому, если вы размещаете свой код на GitHub, Travis-CI — отличное средство, чтобы начать использовать непрерывную интеграцию. Travis-CI может собрать ваш код на виртуальной машине, на которой запущены Linux, OS X или iOS.
Для того чтобы начать работу, добавьте файл в расширением. travis.yml в ваш репозиторий. В качестве примера его содержимого приведем следующий код:
language: python
python:
··- "2.6"
··- "2.7"
··- "3.3"
··- "3.4"
script: python tests/test_all_of_the_units.py
branches:
··only:
····- master
Этот код указывает протестировать ваш проект для всех перечисленных версий Python путем запуска заданного сценария, сборка будет выполнена только для ветки master. Существует множество доступных параметров вроде уведомлений, предыдущих и последующих шагов и многих других (http://about.travis-ci.org/docs/). Для того чтобы использовать Tox вместе с Travis-CI, добавьте сценарий Tox в ваш репозиторий и измените строку с конструкцией script:; файл должен выглядеть так:
install:
··- pip install tox
script:
··- tox
Чтобы активизировать тестирование для вашего проекта, перейдите на сайт https://travis-ci.org/ и авторизуйтесь с помощью вашей учетной записи для GitHub. Далее активизируйте ваш проект в настройках профиля, и вы готовы к работе. С этого момента тесты для вашего проекта будут запускаться после каждой отправки кода в GitHub.
Jenkins CI (http://jenkins.io/) — это расширяемый движок непрерывной интеграции, в данный момент он считается самым популярным. Работает на Windows, Linux и OS X, его можно подключить к «каждому существующему инструменту управления исходным кодом». Jenkins является сервлетом Java (эквивалент приложений WSGI в Java), который поставляется с собственным контейнером сервлетов, что позволяет вам запускать его с помощью команды java — jar jenkins.war. Для получения более подробной информации обратитесь к инструкциям по установке Jenkins (https://wiki.jenkins-ci.org/display/JENKINS/Installing+Jenkins); на странице Ubuntu содержится информация о том, как разместить Jenkins на базе обратного прокси Apache или Nginx.
Вы взаимодействуете с Jenkins с помощью информационной панели или его RESTful API[97], основанного на НТТР (например, по адресу http://myServer:8080/api), что означает, что можно использовать HTTP для того, чтобы общаться с Jenkins с удаленных машин. Например, взгляните на Jenkins Dashboard (https://builds.apache.org/) для Apache или Pylons (http://jenkins.pylonsproject.org/).
Для взаимодействия с Jenkins API в Python наиболее часто применяется python-jenkins (https://pypi.python.org/pypi/python-jenkins), созданный командой OpenStack[98] (https://www.openstack.org/). Большинство пользователей Python конфигурируют Jenkins так, чтобы он запускал сценарий Tox как часть процесса сборки. Для получения более подробной информации обратитесь к документации по адресу http://tox.readthedocs.io/en/latest/example/jenkins.html, где рассказывается об использовании Tox для Jenkins, а также к руководству http://tinyurl.com/jenkins-setup-master-slave по настройке Jenkins для работы с несколькими машинами.
Buildbot (http://docs.buildbot.net/current/) — это система Python, предназначенная для автоматизации цикла компиляции/тестирования, проверяющего изменения кода. Похож на Jenkins тем, что опрашивает менеджер системы контроля версий на наличие изменений, выполняет сборку и тестирование вашего кода на нескольких компьютерах в соответствии с вашими инструкциями (имеет встроенную поддержку для Tox), а затем говорит вам, что произошло. Он работает на базе веб-сервера Twisted. Если вам нужен пример того, как будет выглядеть веб-интерфейс, взгляните на общедоступную информационную панель buildbot от Chromium (https://build.chromium.org/p/chromium/waterfall) (с помощью Chromium работает браузер Chrome).
Поскольку Buildbot написан на чистом Python, его можно установить с помощью pip:
$ pip install buildbot
Версия 0.9 имеет REST API (http://docs.buildbot.net/latest/developer/apis.html), но она все еще находится на стадии бета-тестирования, поэтому вы не сможете ее использовать, если только явно не укажете номер версии (например, pip install buildbot==0.9.00.9.0rc1). Buildbot имеет репутацию самого мощного и самого сложного инструмента непрерывной интеграции. Для начала работы с ним обратитесь к этому отличному руководству: http://docs.buildbot.net/current/tutorial.
Salt, Ansible, Puppet, Chef и CFEngine — это инструменты для автоматизации сервера, которые предоставляют системным администраторам элегантный способ управлять их флотом физических и виртуальных машин. Все они могут управлять машинами, на которых установлены Linux, Unix-подобные системы, а также Windows.
Конечно, мы можем использовать только Salt и Ansible, поскольку они написаны на Python. Но они относительно новые, другие же варианты применяются более широко. В следующих разделах проведен их краткий обзор.
Разработчики Docker ожидают, что инструменты по автоматизации систем вроде Salt, Ansible и прочих будут дополнены Docker, а не заменены им. Взгляните на статью http://stackshare.io/posts/how-docker-fits-into-the-current-devops-landscape о том, как Docker работает с остальным ПО для DevOps.
Salt (http://saltstack.org/) называет свой главный узел мастером, а узлы-агенты — миньонами или хостами-миньонами. Его основная цель — высокая скорость; работа с сетью по умолчанию выполняется с помощью ZeroMQ, между мастером и миньонами устанавливается соединение TCP. Члены команды разработчиков Salt написали свой (необязательный) протокол передачи данных RAET (https://github.com/saltstack/raet), который работает быстрее, чем TCP, и теряет не так много данных, как UDP.
Salt поддерживает версии Python 2.6 и 2.7, его можно установить с помощью команды pip:
$ pip install salt # Для Python 3 версии пока нет…
После конфигурирования сервера-мастера и любого количества хостов-миньонов мы можем запускать для миньонов произвольные команды оболочки или использовать заранее созданные модули, состоящие из сложных команд. Следующая команда перечисляет всех доступных миньонов с помощью команды ping из модуля тестирования salt:
$ salt '*' test.ping
Вы можете отфильтровать хосты-миньоны либо по их идентификатору, либо с помощью системы grains (http://docs.saltstack.org/en/latest/topics/targeting/grains.html), которая использует статическую информацию хоста вроде версии операционной системы или архитектуры ЦП, чтобы предоставить таксономию хостов для модулей Salt. Например, следующая команда применяет систему grains для перечисления только тех миньонов, на которых запущена CentOS:
$ salt — G 'os: CentOS' test.ping
Salt также предоставляет систему состояний. Состояния могут быть использованы для конфигурирования хостов-миньонов. Например, когда миньону указывается прочесть следующий файл состояний, он установит и запустит сервер Apache:
apache:
··pkg:
····- installed
service:
····- running
····- enable: True
····- require:
······- pkg: apache
Файлы состояний могут быть написаны с помощью YAML, дополненной системой шаблонов Jinja2, или же могут быть чистыми модулями Python. Для получения более подробной информации обратитесь к документации Salt по адресу http://docs.saltstack.com/.
Самое большое преимущество Ansible (http://ansible.com/) перед другими системами автоматизации — для ее установки на клиентских машинах не требуется ничего (кроме Python). Все другие варианты[99] поддерживают на клиентах демонов, которые опрашивают мастера. Их конфигурационные файлы имеют формат YAML. Сценарии — это документы для конфигурирования, развертывания и управления для Ansible, они написаны на YAML и используют для шаблонов язык Jinja2. Ansible поддерживает версии Python 2.6 и 2.7, ее можно установить с помощью pip:
$ pip install ansible # Для Python 3 версии пока нет…
Ansible требует наличия файла инвентаря, в котором описывается, к каким хостам он имеет доступ. В следующем фрагменте кода показывается пример хоста и сценария, который опрашивает все хосты в файле инвентаря. Рассмотрим пример файла инвентаря (hosts.yml):
[server_name]
127.0.0.1
Рассмотрим пример сценария (ping.yml):
-
- hosts: all
··tasks:
····- name: ping
····action: ping
Для того чтобы запустить сценарий, введите следующую команду:
$ ansible-playbook ping.yml — i hosts.yml — ask-pass
Сценарий Ansible будет опрашивать все серверы, перечисленные в файле hosts.yml. С помощью Ansible вы можете выбрать группы серверов. Для получения более подробной информации об Ansible прочтите документацию к ней (http://docs.ansible.com/). Руководство по Ansible Servers for Hackers (https://serversforhackers.com/an-ansible-tutorial/) также содержит подробную информацию.
Puppet написан на Ruby и предоставляет собственный язык — PuppetScript — для конфигурирования. Имеет выделенный сервер Puppet Master, который отвечает за управление узлами-агентами. Модули — это небольшие разделяемые единицы кода, написанные для автоматизации или определения состояния системы. Puppet Forge (https://forge.puppetlabs.com/) — это репозиторий для модулей, написанных сообществом для Open Source Puppet и Puppet Enterprise.
Узлы-агенты отправляют основную информацию о системе (например, операционную систему, ядро, архитектуру, IP-адрес и имя хоста) Puppet Master. Puppet Master компилирует на ее основе каталог данных о том, как нужно конфигурировать каждый узел, и пересылает его агенту. Агент выполняет изменения на основе того, что указано в каталоге, и отправляет Puppet Master отчет о проделанной работе.
Facter (да, его имя заканчивается на — er) — весьма интересный инструмент, который поставляется с Puppet и получает основную информацию о системе. Вы можете сослаться на эти факты как на переменные при написании модулей Puppet:
$ facter kernel
Linux
$
$ facter operatingsystem
Ubuntu
Процесс написания модулей в Puppet довольно прямолинеен: манифесты Puppet (файлы с расширением *.pp) формируют модули Puppet. Рассмотрим пример приложения Hello World для Puppet:
notify { 'Hello World, this message is getting logged into the agent node':
····#As nothing is specified in the body, the resource title
····#is the notification message by default.
}
Перед вами еще один пример, использующий логику системы. Чтобы обратиться к другим фактам, добавьте знак $ к имени переменной, например $hostname (в нашем случае $operatingsystem):
notify{ 'Mac Warning':
····message => $operatingsystem? {
········'Darwin' => 'This seems to be a Mac.',
········default => 'I am a PC.',
····},
}
Для Puppet существует несколько типов ресурсов, но парадигма «пакет-файл-сервис» — это все, что вам нужно для выполнения большинства задач по управлению конфигурацией. Следующий код Puppet позволяет убедиться, что пакет OpenSSH-Server устанавливается в системе. Сервису sshd (демон сервера SSH) указывается выполнять перезапуск каждый раз, когда изменяется конфигурационный файл sshd:
package { 'openssh-server':
····ensure => installed,
}
file { '/etc/ssh/sshd_config':
····source => 'puppet:///modules/sshd/sshd_config',
····owner => 'root',
····group => 'root',
····mode => '640',
····notify => Service['sshd'], # sshd перезапустится
·······························# после каждого изменения этого файла
····require => Package['openssh-server'],
}
·
service { 'sshd':
····ensure => running,
····enable => true,
····hasstatus => true,
····hasrestart=> true,
}
Для получения более подробной информации обратитесь к документации к Puppet Labs (http://docs.puppetlabs.com/).
Если для управления конфигурацией вы выбираете Chef (https://www.chef.io/chef/), то для написания кода инфраструктуры будете использовать язык Ruby. Chef похож на Puppet, но разработан с противоположной философией: Puppet предоставляет фреймворк, который упрощает работу за счет гибкости, а Chef практически не предоставляет фреймворка (его цель — быть очень гибким, поэтому его сложнее использовать).
Клиенты Chef работают на каждом узле вашей инфраструктуры и регулярно опрашивают сервер Chef, чтобы гарантировать, что ваша система всегда находится в рабочем состоянии.
Каждый отдельный клиент Chef конфигурирует себя самостоятельно. Такой подход делает Chef масштабируемой платформой по автоматизации.
Chef для работы использует пользовательские рецепты (элементы конфигурации), реализованные в cookbooks.Cookbooks, которые, по сути, являются пакетами для инфраструктурного выбора и обычно хранятся на сервере Chef. Прочтите серию руководств от DigitalOcean, посвященных Chef (http://tinyurl.com/digitalocean-chef-tutorial), чтобы узнать, как создать просто сервер Chef.
Используйте команду knife для создания поваренной книги:
$ knife cookbook create cookbook_name
Статья Энди Гейла (Andy Gale) Getting started with Chef (http://gettingstartedwithchef.com/first-steps-with-chef.html) — хорошая стартовая точка для тех, кто начинает работать с Chef.
Множество поваренных книг сообщества вы можете найти в Chef Supermarket — с их помощью вы легко сможете начать писать собственные поваренные книги. Для получения более подробной информации обратитесь к полной документации Chef (https://docs.chef.io/).
CFEngine имеет крошечный отпечаток, поскольку написан на C. Основная цель ее проекта — отказоустойчивость. Она достигается с помощью автономных агентов, работающих в распределенной сети (в противоположность архитектуре мастер/клиент), которые общаются с использованием теории обещаний (https://en.wikipedia.org/wiki/Promise_theory). Если вам нужна архитектура без мастера, попробуйте эту систему.
Следующие библиотеки помогут всем системным администраторам наблюдать запущенные задачи, но их приложения значительно отличаются друг от друга: Psutil предоставляет информацию в Python, которая может быть получена вспомогательными функциями Unix, Fabric позволяет легко определить и выполнить команды для набора удаленных хостов с помощью SSH, а Luigi помогает планировать запуск и наблюдение за долгоиграющими пакетными процессами вроде цепочек команд Hadoop.
Psutil (https://pythonhosted.org/psutil/) — это кросс-платформенный (включая Windows) интерфейс для разного рода системной информации (например, ЦП, памяти, дисков, сети, пользователей и процессов). Позволяет с помощью Python получить доступ к информации, которую многие из нас привыкли получать, используя команды Unix (https://en.wikipedia.org/wiki/List_of_Unix_commands) вроде top, ps, df и netstat. Установите его с помощью pip:
$ pip install psutil
Рассмотрим пример, который наблюдает за перегрузкой сервера (если какой-то тест — сети или ЦП — даст сбой, он отправит электронное письмо):
# Функции для получения значений системы:
from psutil import cpu_percent, net_io_counters
# Функции для перерыва:
from time import sleep
# Пакет для сервисов электронной почты:
import smtplib
import string
MAX_NET_USAGE = 4 00000
MAX_ATTACKS = 4
attack = 0
counter = 0
while attack <= MAX_ATTACKS:
····sleep(4)
····counter = counter + 1
····# Проверяем использование ЦП
····if cpu_percent(interval = 1) > 70:
········attack = attack + 1
····# Проверяем использование сети
····neti1 = net_io_counters()[1]
····neto1 = net_io_counters()[0]
····sleep(1)
····neti2 = net_io_counters()[1]
····neto2 = net_io_counters()[0]
····# Рассчитываем байты в секунду
····net = ((neti2+neto2) — (neti1+neto1))/2
····if net > MAX_NET_USAGE:
········attack = attack + 1
····if counter > 25:
········attack = 0
········counter = 0
# Пишем очень важное электронное письмо, если значение параметра attack больше 4
TO = "you@your_email.com"
FROM = "webmaster@your_domain.com"
SUBJECT = "Your domain is out of system resources!"
text = "Go and fix your server!"
BODY = string.join(
·····("From: %s" %FROM,"To: %s" %TO,"Subject: %s" %SUBJECT, "",text), "\r\n")
server = smtplib.SMTP('127.0.0.1')
server.sendmail(FROM, [TO], BODY)
server.quit()
Хорошим примером использования Psutil является glances (https://github.com/nicolargo/glances/) — полностью консольное приложение, которое ведет себя как расширенная версия top (перечисляет запущенные процессы, упорядочивая их по использованию ЦП или другим способом, указанным пользователем) и имеет возможность наблюдать за клиентом и сервером.
Fabric (http://docs.fabfile.org/) — это библиотека, предназначенная для упрощения задач системного администратора. Позволяет соединяться с помощью SSH с несколькими хостами и выполнять задачи для каждого из них. Это удобно для системных администраторов, а также для тех, кто развертывает приложения. Чтобы установить Fabric, используйте pip:
$ pip install fabric
Рассмотрим полный модуль Python, определяющий две задачи Fabric: memory_usage и deploy:
# fabfile.py
from fabric.api import cd, env, prefix, run, task
env.hosts = ['my_server1', 'my_server2'] # С чем соединяться по SSH
@task
def memory_usage():
····run('free — m')
@task
def deploy():
····with cd('/var/www/project-env/project'):
········with prefix('../bin/activate'):
············run('git pull')
············run('touch app.wsgi')
Оператор with вкладывает команды друг в друга, поэтому в конечном счете метод deploy() для каждого хоста начинает выглядеть так:
$ ssh имя_хоста cd /var/ww/project-env/project &&../bin/activate && git pull
$ ssh имя_хоста cd /var/ww/project-env/project &&../bin/activate && \
> touch app.wsgi
Учитывая предыдущий код, сохраненный в файле fabfile.py (имя модуля по умолчанию, которое ищет fab), мы можем проверить использование памяти с помощью нашей новой задачи memory_usage:
$ fab memory_usage
[my_server1] Executing task 'memory'
[my_server1] run: free — m [my_server1] out:················ total···· used···· free·· shared· buffers·· cached
[my_server1] out: Mem:··········· 6964···· 1897···· 5067······· 0····· 166····· 222
[my_server1] out: — /+ buffers/cache:······ 1509···· 5455
[my_server1] out: Swap:············· 0······· 0······· 0
[my_server2] Executing task 'memory'
[my_server2] run: free — m [my_server2] out:················ total···· used···· free·· shared· buffers·· cached
[my_server2] out: Mem:··········· 1666····· 902····· 764······· 0····· 180····· 572
[my_server2] out: — /+ buffers/cache:······· 148···· 1517
[my_server2] out: Swap:··········· 895······· 1····· 894
И мы можем выполнить развертывание:
$ fab deploy
Дополнительная функциональность включает параллельное исполнение, взаимодействие с удаленными программами и группирование хостов. В документации к Fabric (http://docs.fabfile.org/) приведены понятные примеры.
Luigi (https://pypi.python.org/pypi/luigi) — это инструмент для управления конвейером, разработанный и выпущенный компанией Spotify. Помогает разработчикам управлять целым конвейером крупных долгоиграющих пакетных задач, объединяя запросы Hive, запросы к базе данных, задачи Hadoop Java, задачи pySpark и многие другие задачи, которые вы можете написать самостоятельно. Они не должны быть приложениями, работающими с большими данными, API позволяет запланировать что угодно. Spotify позволил запускать задачи с помощью Hadoop, поэтому разработчики предоставляют все необходимые вспомогательные программы в пакете luigi.contrib (http://luigi.readthedocs.io/en/stable/api/luigi.contrib.html). Установите его с помощью pip:
$ pip install luigi
Включает в себя веб-интерфейс, поэтому пользователи могут фильтровать свои задачи и просматривать графики зависимостей для рабочего потока конвейера и их продвижение. В репозитории GitHub вы можете найти примеры задач Luigi, также можете просмотреть документацию к Luigi.
В этом разделе перечислены наиболее популярные способы оптимизации скорости, используемые сообществом Python. В табл. 8.1 показаны варианты оптимизации, которые вы можете применить после того, как попробуете простые методы вроде запуска профайлера (https://docs.python.org/3.5/library/profile.html) и сравнения параметров для сниппетов кода (https://docs.python.org/3.5/library/timeit.html).
Вы, возможно, уже слышали о глобальной блокировке интерпретатора (global interpreter lock, GIL) (http://wiki.python.org/moin/GlobalInterpreterLock). Это способ, с помощью которого реализация C для Python позволяет нескольким потокам работать одновременно.
Управление памятью в Python не полностью потокобезопасно, поэтому GIL нужен для того, чтобы помешать нескольким потокам запускать одновременно один и тот же код.
GIL зачастую называют ограничением Python, но он не является такой уж большой проблемой — мешает лишь тогда, когда процессы связаны с ЦП (в этом случае, как и для NumPy или криптографических библиотек, которые мы рассмотрим далее, код переписан на C со связыванием с Python). Для всего прочего (для ввода/вывода в сетях или файлах) узким местом является код, блокирующий один поток, ожидающий завершения операции ввода/вывода. Вы можете решить проблему с блокировкой с помощью потоков или событийно-ориентированного программирования.
Отметим, что в Python 2 имеются медленная и быстрая версии библиотек — StringIO и cStringIO, ElementTree и cElementTree. Реализации на C работают быстрее, но их нужно явно импортировать. Начиная с версии Python 3.3 обычные версии импортируют быструю реализацию там, где это возможно, а библиотеки, чье имя начинается с C, считаются устаревшими.
Способ | Лицензия | Причины использовать |
---|---|---|
Многопоточность | PSFL | Позволяет создавать несколько потоков выполнения. Многопоточность (при использовании CPython из-за наличия GIL) не задействует собственные процессы; разные потоки переключаются, когда один из них заблокирован (это полезно, когда узким местом является какая-нибудь блокирующая задача вроде ожидания окончания операции ввода/вывода). GIL отсутствует в некоторых других реализациях Python вроде Jython и IronPython |
Multiprocessing/subprocess | PSFL | Инструменты библиотеки multiprocessing позволяют создавать другие процессы Python, минуя GIL. Subprocess позволяет запускать несколько процессов командной строки |
PyPy | Лицензия MIT | Этот интерпретатор Python (в данный момент для версии Python 2.7.10 или 3.2.5) предоставляет возможность динамической компиляции в код на языке С там, где это возможно. |
Не требует усилий: вам не нужно писать код, при этом он дает значительный прирост скорости. Полноценная замена для CPython, которая обычно работает (любые библиотеки, написанные на С, должны использовать CFFI или находиться в списке совместимости PyPy (http://pypy.org/compat.html)) | ||
Cython | Лицензия Apache | Предоставляет два способа статически скомпилировать код Python: использование языка аннотаций Cython (*.pxd); статистическое компилирование кода на чистом Python и применение декораторов Cython для указания типа объекта |
Numba | Лицензия BSD | Предоставляет статический (благодаря инструменту pycc) и динамический компиляторы, компилирующие код в машинный код. Использует массивы NumPy. Требуется версия Python 2.7 или 3.4, библиотека llvmlite (http://llvmlite.pydata.org/en/latest/install/index.html) и ее зависимость, инфраструктура LLVM (Low-Level Virtual Machine) |
Weave | Лицензия BSD | Предоставляет способ «сплести» несколько строк кода на C в код на Python (применяйте его только в том случае, если вы уже используете Weave). В противном случае используйте Cython — Weave считается устаревшим |
PyCUDA/gnumpy/ TensorFlow/ Theano/PyOpenCL | MIT/модифицированная лицензия BSD/BSD/BSD/MIT | Эти библиотеки предоставляют разные способы использования NVIDIA GPU, если он у вас установлен, и могут установить набор инструментов для CUDA (http://docs.nvidia.com/cuda/). PyOpenCL может использовать процессоры не только от NVIDIA. Для каждого из них существует собственное приложение, например gnumpy предполагается как полноценная замена для NumPy |
Непосредственное использование библиотек C/C++ | — | Повышение скорости стоит того, чтобы потратить дополнительное время на написание кода на C/C++ |
Джефф Напп (Jeff Knupp), автор книги Writing Idiomatic Python (http://bit.ly/writing-idiomatic-python), написал статью о том, как обойти GIL (http://bit.ly/pythons-hardest-problems), процитировав статью Дэвида Бизли (David Beazley)[100] на эту тему.
Многопоточность и другие способы оптимизации, показанные в табл. 8.1, более подробно рассматриваются в следующих разделах.
Библиотека для работы с потоками в Python позволяет создать несколько потоков. Из-за GIL (во всяком случае в CPython) только один процесс Python может быть запущен для каждого интерпретатора; это означает, что прирост производительности появится только в том случае, если хотя бы один поток заблокирован (например, для ввода/вывода). Еще один вариант для ввода/вывода — обработка событий. Чтобы узнать подробнее, прочтите абзацы, связанные с asyncio, в подразделе «Производительность сетевых инструментов из стандартной библиотеки Python» раздела «Распределенные системы» главы 9.
Когда у вас есть несколько потоков Python, ядро замечает, что один из потоков заблокирован для ввода-вывода, и оно переключается, чтобы позволить следующему потоку использовать процессор до тех пор, пока он не будет заблокирован или не завершится. Все это происходит автоматически, когда вы запускаете ваши потоки. Есть хороший пример применения многопоточности на сайте Stack Overflow (http://bit.ly/threading-in-python), для серии Python Module of the Week написана отличная статья на тему многопоточности (https://pymotw.com/2/threading/). Вы также можете просмотреть документацию стандартной библиотеки, посвященную работе с потоками (https://docs.python.org/3/library/threading.html).
Модуль multiprocessing (https://docs.python.org/3/library/multiprocessing.html) стандартной библиотеки Python предоставляет способ обойти GIL путем запуска дополнительных интерпретаторов Python. Отдельные процессы могут общаться друг с другом с помощью запросов multiprocessing.Pipe или multiprocessing.Queue; также они могут делиться памятью с помощью запросов multiprocessing.Array или multiprocessing.Value, что автоматически реализует блокировки. Осторожно делитесь данными; эти объекты реализуют блокировку для того, чтобы предотвратить одновременный доступ разных процессов.
Рассмотрим пример, иллюстрирующий прирост скорости, который появляется в результате использования пула работников. Существует компромисс между сэкономленным временем и временем, затрачиваемым на переключение между интерпретаторами. В примере используется метод Монте-Карло для оценки значения числа пи[101]:
>>> import multiprocessing
Использование multiprocessing.Pool внутри менеджера контекста указывает, что пул должен применяться только тем процессом, который его создал.
Общее количество итераций останется неизменным, оно лишь будет поделено на разное количество процессов.
Метод pool.map() создает несколько процессов — по одному на каждый элемент списка итераций; максимальное количество равно числу, указанному при инициализации пула (в вызове multiprocessing.Pool(processes)).
Существует только один процесс для первого испытания timeit.
10 повторений одного процесса с 10 миллионами итераций заняли 134 секунды.
Для второго испытания timeit создано 10 процессов.
10 повторений десяти процессов, каждый из которых имеет один миллион итераций, заняли 74 секунды.
Идея заключается в том, что существуют накладные расходы при создании нескольких процессов, но инструменты, позволяющие запустить с помощью Python несколько процессов, довольно надежны. Для получения более подробной информации просмотрите документацию о библиотеке multiprocessing в стандартной библиотеке (https://docs.python.org/3.5/library/multiprocessing.html), а также прочитайте статью Джеффа Наппа (Jeff Knupp) о том, как обойти GIL (пара абзацев посвящены этой библиотеке) (http://bit.ly/pythons-hardest-problems).
Библиотека subprocess (https://docs.python.org/3/library/subprocess.html) была представлена в версии стандартной библиотеки для Python 2.4, определена в PEP 324 (https://www.python.org/dev/peps/pep-0324). Выполняет системный вызов (вроде unzip или curl), как если бы она была вызвана из командной строки (по умолчанию, не вызывая системную оболочку (http://bit.ly/subprocess-security)), а разработчик выбирает, что нужно сделать с входным и выходным конвейерами subprocess. Мы рекомендуем пользователям Python 2 получить обновленную версию пакета subprocess32, в которой исправляются некоторые ошибки. Установите его с помощью pip:
$ pip install subprocess32
В блоге Python Module of the Week вы можете найти отличное руководство по subprocess (https://pymotw.com/2/subprocess/).
PyPy — это реализация Python на чистом Python. Она быстра; и когда она работает, вам не нужно больше ничего делать со своим кодом — он будет работать без дополнительных усилий. Вам следует воспользоваться этим вариантом в первую очередь.
Вы не можете получить ее с помощью команды pip, поскольку, по сути, это еще одна реализация Python. На странице загрузок PyPy http://pypy.org/download.html вы можете найти корректную версию реализации для вашей версии Python и операционной системы.
Существует модифицированная версия тестового кода, ограниченная по процессору, от Дэвида Бизли (David Beazley) (http://www.dabeaz.com/GIL/gilvis/measure2.py), в которую добавлен цикл для выполнения нескольких тестов. Вы можете увидеть разницу между PyPy и CPython.
Сначала запустим ее с помощью CPython:
$ # CPython
$./python — V Python 2.7.1
$
$./python measure2.py
1.0 67744 01665 1.4 54123 97385 1.5 14852 04697 1.5 46938 89618 1.6 01091 14647
А теперь запустим тот же сценарий, но изменим интерпретатор Python — выберем PyPy:
$ # PyPy
$./pypy — V Python 2.7.1 (7773f8fc4223, Nov 18 2011, 18:47:10)
[PyPy 1.7.0 with GCC 4.4.3]
$
$./pypy measure2.py
0.068 39990 61584 0.048 32100 86823 0.038 85889 05334 0.044 06905 17426 0.069 53001 02234
Получается, что благодаря простой загрузке PyPy мы сократили время работы сценария с 1,4 секунды до 0,05 — практически в 20 раз. Порой ваш код будет ускорен менее чем в два раза, но иногда вы сможете значительно его ускорить. И для этого не нужно прикладывать никаких усилий, за исключением загрузки интерпретатора PyPy. Если хотите, чтобы ваша библиотека, написанная на C, была совместима с PyPy, следуйте советам PyPy (http://pypy.org/compat.html) и используйте CFFI вместо ctypes из стандартной библиотеки.
К сожалению, PyPy работает не со всеми библиотеками, использующими расширения, написанные на C. Для этих случаев Cython (произносится «сайтон» — это не то же самое, что CPython, стандартная реализация Python, созданная с помощью C) (http://cython.org/) реализует superset языка Python (можно писать модули Python на C и C++). Cython дает возможность вызывать функции из скомпилированных библиотек на C и предоставляет контекст nogil, позволяющий обойти GIL для раздела кода (он не манипулирует объектами Python) (http://tinyurl.com/cython-nogil). Применяя Cython, вы можете воспользоваться преимуществами строгого типизирования в Python[102] переменных и операций.
Рассмотрим пример строгой типизации с помощью Cython:
def primes(int kmax):
"""Расчет простых чисел с помощью дополнительных ключевых слов Cython """
····cdef int n, k, i
····cdef int p[1000]
····result = []
····if kmax > 1000:
········kmax = 1000
····k = 0
····n = 2
····while k < kmax:
········i = 0
········while i < k and n % p[i]!= 0:
············i = i + 1
········if i == k:
············p[k] = n
············k = k + 1
············result.append(n)
········n = n + 1
····return result
Эта реализация алгоритма поиска простых чисел содержит дополнительные ключевые слова. Следующий пример написан на чистом Python:
def primes(kmax):
""" Расчет простых чисел с помощью стандартного синтаксиса Python """
····p= range(1000)
····result = []
····if kmax > 1000:
········kmax = 1000
····k = 0
····n = 2
····while k < kmax:
········i = 0
········while i < k and n % p[i]!= 0:
············i = i + 1
········if i == k:
············p[k] = n
············k = k + 1
············result.append(n)
········n = n + 1
····return result
Обратите внимание: в версии Cython вы объявляете, что целые числа и массивы целых чисел будут скомпилированы в типы C, в то же время будет создан список Python:
Тип объявляется как целое число.
Переменные n, k и i объявляются как целые числа.
Мы заранее выделяем память для массива целых чисел p размером в 1000 элементов.
В чем же разница? В версии Cython вы можете увидеть объявление типов переменных и массива целых чисел, которые выглядят так же, как и в обычном С. Например, дополнительное объявление типа (целочисленного) в выражении cdef int n,k,i позволяет компилятору Cython генерировать более эффективный код С. Поскольку синтаксис несовместим со стандартным Python, он сохраняется в файлах с расширением *.pyx, а не с расширением *.py.
Каковы различия в скорости? Давайте проверим!
Модуль pyximport позволяет импортировать файлы с расширением *.pyx (например, primesCy.pyx) с помощью скомпилированной в Cython версии функции primes.
Команда pyximport.install() позволяет интерпретатору Python непосредственно запустить компилятор Cython для генерации кода C, который автоматически компилируется в библиотеку с расширением *.so. Далее Cython может легко и эффективно импортировать эту библиотеку в ваш код Python.
С помощью функции time.time() вы можете сравнить время выполнения этих двух вызовов, которые определяют 500 простых чисел. На стандартном ноутбуке (dual-core AMD E-450 1.6 GHz) мы получили следующие значения:
Cython time: 0.0054 seconds
Python time: 0.0566 seconds
А здесь результат работы встроенной машины ARM BeagleBone (http://beagleboard.org/Products/BeagleBone):
Cython time: 0.0196 seconds
Python time: 0.3302 seconds
Numba (http://numba.pydata.org/) — это компилятор для Python, поддерживающий NumPy (он является динамическим — just-in-time (JIT)). Компилирует аннотированный код Python (и NumPy) для LLVM (Low-Level Virtual Machine) (http://llvm.org/) с помощью особых декораторов. Вкратце, Numba использует LLVM для компилирования кода Python в машинный код, который может быть нативно выполнен во время работы программы.
Если вы используете Anaconda, установите Numba с помощью команды conda install numba. Если нет, установите его вручную. Вы должны заранее установить NumPy и LLVM (перед Numba).
Проверьте, какая версия LLVM вам нужна (на странице PyPI для llvmlite по адресу https://pypi.python.org/pypi/llvmlite), и загрузите ее для вашей ОС:
• сборки LLVM для Windows (http://llvm.org/builds/);
• сборки LLVM для Debian/Ubuntu (http://llvm.org/apt/);
• сборки LLVM для Fedora (https://apps.fedoraproject.org/packages/llvm);
• вы можете найти информацию о том, как устанавливать LLVM на основе исходного кода для других систем Unix, в разделе «Сборка компиляторов Clang + LLVM» (http://ftp.math.utah.edu/pub/llvm/);
• для OS X используйте команду brew install homebrew/versions/llvm37 (или выберите текущую версию).
Как только вы установите LLVM и NumPy, инсталлируйте Numba с помощью pip. Вам может понадобиться помочь установщику найти файл llvm-config, предоставив переменной среды LLVM_CONFIG соответствующий путь, например:
$ LLVM_CONFIG=/path/to/llvm-config-3.7 pip install numba
Чтобы использовать его в своем коде, декорируйте свои функции:
Без аргументов декоратор @jit выполняет ленивую компиляцию — сам решает, оптимизировать ли функцию и как это сделать.
Для ранней компиляции укажите типы. Функция будет скомпилирована с указанной специализацией, ни одну другую специализацию использовать не получится, возвращаемое значение и два аргумента будут иметь тип numba.int32.
Флаг nogil позволит коду игнорировать глобальную блокировку интерпретатора, а модуль numba.pycc можно использовать для компилирования кода заранее. Для получения более подробной информации обратитесь к руководству пользователя для Numba (http://numba.pydata.org/numba-doc/latest/user).
Numba опционально может быть создан с той производительностью, которая позволит ему работать на графическом процессоре (graphics processing unit, GPU), оптимизированном для выполнения быстрых параллельных вычислений в современных видеоиграх. Вам понадобится NVIDIA GPU, также нужно установить тулкит CUDA Toolkit от NVIDIA (https://developer.nvidia.com/cuda-downloads). Далее следуйте инструкциям документации по использованию Numba’s CUDA JIT для GPU (http://numba.pydata.org/numba-doc/0.13/CUDAJit.html).
Помимо Numba, еще одной популярной библиотекой, которая может работать с GPU, является TensorFlow (https://www.tensorflow.org/). Выпущена компанией Google под лицензией Apache v2.0. Предоставляет возможность использовать тензоры (многомерные матрицы), а также способ объединить в цепочку операции над ними для более быстрого выполнения операций над матрицами.
На данный момент она может использовать GPU только в операционных системах Linux. Для получения более подробных инструкций обратитесь к следующим страницам:
• установка TensorFlow с поддержкой GPU — http://bit.ly/tensorflow-gpu-support;
• установка TensorFlow без поддержки GPU — http://bit.ly/tensorflow-no-gpu.
Среди тех, кто не пользуется Linux, до того как компания Google опубликовала TensorFlow (http://deeplearning.net/software/theano/), привычным вариантом работы с матричной математикой с помощью GPU была библиотека Theano, активно разрабатываемая в Монреальском университете. Для нее создана страница, посвященная использованию GPU (http://deeplearning.net/software/theano/tutorial/using_gpu.html). Theano поддерживает операционные системы Windows, OS X и Linux. Доступна по команде pip:
$ pip install Theano
Для низкоуровневого взаимодействия с GPU вы можете использовать PyCUDA (https://developer.nvidia.com/pycuda).
Наконец, те, у кого нет NVIDIA GPU, могут использовать PyOpenCL (https://pypi.python.org/pypi/pyopencl), обертку для библиотеки OpenCL от Intel (https://software.intel.com/en-us/intel-opencl), которая совместима с несколькими разными аппаратными наборами (https://software.intel.com/en-us/articles/opencl-drivers).
Все библиотеки, описанные в следующих разделах, отличаются друг от друга. CFFI и ctypes написаны на Python, F2PY нужна для FORTRAN, SWIG позволяет использовать объекты языка C во многих языках (не только Python), а Boost.Python (библиотека C++) — объекты языка C++ в коде Python и наоборот. В табл. 8.2 приводится более подробная информация.
Библиотека | Лицензия | Причины использовать |
---|---|---|
CFFI | Лицензия MIT | Лучшая совместимость с PyPy. Позволяет писать код С изнутри Python, который может быть скомпилирован для сборки общей библиотеки С со связыванием Python |
ctypes | Лицензия Python Software Foundation | Находится в стандартной библиотеке Python. Позволяет оборачивать существующие DLL или общие объекты, которые писали не вы. Вторая по качеству совместимость с PyPy |
F2PY | Лицензия BSD | Позволяет использовать библиотеку FORTRAN. F2PY является частью библиотеки NumPy, поэтому вам следует применять NumPy |
SWIG | GPL (output не ограничен) | Позволяет автоматически генерировать библиотеки на многих языках, используя специальный формат файла, который не похож ни на C, ни на Python |
Boost.Python | Лицензия Boost Software | Этот инструмент не относится к командной строке. Это библиотека С++, которая может быть включена в код С++ и использована для определения того, какие объекты должны быть доступны Python |
Пакет CFFI (https://cffi.readthedocs.org/en/latest/) предоставляет простой механизм для взаимодействия с кодом, написанным на C, из Python и PyPy. CFFI рекомендуется использовать с PyPy (http://doc.pypy.org/en/latest/extending.html) для наилучшей совместимости между CPython и PyPy. Он поддерживает два режима: встроенный режим совместимости для бинарных интерфейсов приложения (application binary interface, ABI) (смотрите следующий пример кода) позволяет динамически загружать и запускать функции из исполняемых модулей (по сути, предоставляет такую же функциональность, как LoadLibrary или dlopen), а также режим API, который позволяет выполнять сборку модулей расширения C[103].
Установите его с помощью pip:
$ pip install cffi
Рассмотрим пример взаимодействия ABI:
Строка может быть получена из объявления функции, расположенного в заголовочном файле С.
Открываем общую библиотеку (*.DLL или *.so).
Теперь мы можем относиться к clib как к модулю Python и просто вызываем функции, которые определили с помощью точечной нотации.
ctypes (https://docs.python.org/3/library/ctypes.html) — это выбор де-факто для взаимодействия с кодом на C/C++ и CPython; находится в стандартной библиотеке. Предоставляет полный доступ к нативному интерфейсу на С для большей части операционных систем (например, kernel32 для Windows, или libc для *nix), а также поддерживает загрузку и взаимодействие с динамическими библиотеками — разделяемыми объектами (*.so) или DLL — во время выполнения программы. Вместе с ctypes поставляется множество типов для взаимодействия с API системы, что позволяет вам легко определить собственные сложные типы вроде структур и объединений, а также модифицировать элементы вроде внутренних полей и выравнивания, если это нужно. Она может быть несколько неудобна в использовании (поскольку вам нужно вводить много дополнительных символов), но вместе с модулем стандартной библиотеки struct (https://docs.python.org/3.5/library/struct.html) у вас, по сути, будет полный контроль над тем, как ваши типы данных преобразуются в типы, которые могут применять методы, написанные на чистом C/C++.
Например, структура C, определенная следующим образом в файле my_struct.h:
struct my_struct {
····int a;
····int b;
};
может быть реализована так, как показано в файле с именем my_struct.py:
import ctypes
class my_struct(ctypes.Structure):
····_fields_ = [("a", c_int),
················("b", c_int)]
Генератор интерфейса Fortran-к-Python (F2PY) (http://docs.scipy.org/doc/numpy/f2py/) является частью NumPy. Чтобы его получить, установите NumPy с помощью команды pip:
$ pip install numpy
Предоставляет гибкую функцию командной строки, f2py, которая может быть использована тремя разными способами (все они задокументированы в руководстве к F2PY по адресу http://docs.scipy.org/doc/numpy/f2py/getting-started.html). Если вы имеете доступ к управлению исходным кодом, то можете добавить особые комментарии с инструкциями для F2PY, которые показывают предназначение каждого аргумента (какие элементы являются входными, а какие — возвращаемыми), а затем запустить F2PY:
$ f2py — c fortran_code.f — m python_module_name
В противном случае F2PY способен сгенерировать промежуточный файл с расширением *.pyf, который вы можете модифицировать, чтобы получить такой же результат. Для этого потребуются три шага:
Автоматически сгенерируйте промежуточный файл, который определяет интерфейс между сигнатурами функций языков FORTRAN и Python.
Отредактируйте файл, чтобы входные и выходные переменные были корректно размечены.
Теперь скомпилируйте код и постройте модули расширения.
Упрощенный упаковщик и генератор интерфейсов (Simplified Wrapper Interface Generator, SWIG) (http://www.swig.org/) поддерживает большое количество языков сценария, включая Python. Этот широко распространенный инструмент командной строки генерирует привязки для интерпретируемых языков из аннотированных файлов заголовков C/C++.
Для начала используйте SWIG для того, чтобы автоматически сгенерировать промежуточный файл из заголовка — он будет иметь суффикс *.i. Далее модифицируйте этот файл так, чтобы он отражал именно тот интерфейс, который вам нужен, а затем запустите инструмент сборки, чтобы скомпилировать код в разделяемую библиотеку. Все это описывается шаг за шагом в руководстве по SWIG (http://www.swig.org/tutorial.html).
Несмотря на то что есть некоторые ограничения (в данный момент могут иметься проблемы с небольшим объемом новой функциональности С++, а заставить работать код, содержащий большое количество шаблонов, может быть затруднительно), SWIG предоставляет много функций Python при малых усилиях. В дополнение вы легко можете расширить привязки, создаваемые SWIG (в файле интерфейса), чтобы перегрузить операторы и встроенные методы и, по сути, преобразовать исключения C++ таким образом, дабы вы могли отловить их в Python.
Рассмотрим пример, иллюстрирующий, как переопределить __repr__. Этот фрагмент кода получен из файла с именем MyClass.h:
#include
class MyClass {
private:
····std::string name;
public:
····std::string getName();
};
А это myclass.i:
%include "string.i"
%module myclass
%{
#include
#include "MyClass.h"
%}
%extend MyClass {
····std::string __repr__()
····{
········return $self->getName();
····}
}
%include "MyClass.h"
В репозитории SWIG на GitHub вы можете найти еще больше примеров использования Python (https://github.com/swig/swig/tree/master/Examples/python). Установите SWIG с помощью вашего менеджера пакетов, если он там есть (apt-get install swig, yum install swig.i386 или brew install swig), или воспользуйтесь ссылкой http://www.swig.org/survey.html, чтобы загрузить SWIG, а затем следуйте инструкциям по установке для вашей операционной системы (http://www.swig.org/Doc3.0/Preface.html#Preface_installation). Если у вас нет библиотеки Perl Compatible Regular Expressions (PCRE) в OS X, для ее установки задействуйте Homebrew:
$ brew install pcre
Boost.Python (http://www.boost.org/doc/libs/1_60_0/libs/python/doc/) требует выполнения несколько большего объема ручной работы для того, чтобы воспользоваться функциональностью объектов C++, но он может предложить ту же функциональность, что и SWIG, и даже больше, например обертку, позволяющую получать доступ к объектам Python как к объектам PyObjects в C++, а также инструменты для предоставления доступа к объектам С++ для кода Python. В отличие от SWIG, Boost.Python является библиотекой, а не инструментом командной строки, поэтому вам не нужно создавать промежуточный файл с другим форматированием — все пишется на языке С++. Для Boost.Python написано подробное руководство (http://bit.ly/boost-python-tutorial), если он вам интересен.
В этой главе мы сначала покажем, как применять Python для получения информации из API, которые используются для того, чтобы делиться данными между организациями. Затем мы опишем инструменты, которые многие организации, работающие с Python, применяют с целью поддержки коммуникации внутри своей инфраструктуры.
Мы уже рассмотрели поддержку конвейеров и очередей в Python между процессами в подразделе «Модуль multiprocessing» раздела «Скорость» главы 8. Для коммуникации между компьютерами требуется, чтобы обе стороны применяли заранее определенный набор протоколов: для Интернета применяется стек протоколов TCP/IP[104] (https://en.wikipedia.org/wiki/Internet_protocol_suite). Вы можете реализовать протокол UDP самостоятельно с помощью сокетов (https://pymotw.com/2/socket/udp.html), Python предлагает библиотеки ssl (предоставляет обертки TLS/SSL для сокетов) и asyncio (для реализации асинхронных транспортов для протоколов TCP, UDP, TLS/SSL) (https://docs.python.org/3/library/asyncio-protocol.html), а также конвейеры для подпроцессов.
Но большинство использует более высокоуровневые библиотеки, которые предоставляют клиенты, реализующие различные протоколы уровня приложения: ftplib, poplib, imaplib, nntplib, smtplib, telnetlib и xmlrpc. Все они предоставляют классы для обычных клиентов и клиентов, имеющих обертку TLS/SSL (urllib применяется для работы с запросами HTTP, но мы во многих случаях рекомендуем библиотеку Requests).
В первом разделе этой главы рассматриваются запросы HTTP: как получить данные из общедоступных API в Сети. Далее мы сделаем небольшое отступление и расскажем о сериализации в Python, а в третьем разделе опишем популярные инструменты для работы с сетями предприятий. Мы постараемся явно указывать, когда какой-то инструмент доступен только в Python 3. Если вы используете Python 2 и не можете найти модуль или класс, о котором мы говорим, рекомендуем взглянуть на этот список изменений между стандартными библиотеками Python 2 и Python 3: http://python3porting.com/stdlib.html.
Протокол передачи гипертекста (Hypertext Transfer Protocol, HTTP) — это протокол приложения, предназначенный для распределенных, объединенных информационных систем, использующих гипермедиа. Является основным способом обмена данными во Всемирной сети. Данный раздел посвящен вопросу получения данных из Интернета с помощью библиотеки Requests.
Модуль стандартной библиотеки Python urllib предоставляет большую часть функциональности HTTP, которая вам может понадобиться, но на низком уровне для нее характерно выполнение немало объема работы для решения относительно простых задач (вроде получения данных от сервера HTTPS, который требует аутентификации). В документации к модулю urllib.request говорится, чтобы вы использовали вместо него библиотеку Requests.
Requests работает со всеми запросами HTTP в Python, что позволяет выполнить бесшовную интеграцию с веб-сервисами. Нет необходимости добавлять вручную строки запроса в ваши URL или выполнять кодирование ваших данных для команды POST. Поддержание соединений (устойчивые соединения HTTP) и объединение соединений HTTP в пулы доступны благодаря классу request.sessions.Session, поддерживаемому библиотекой urllib3 (https://pypi.python.org/pypi/urllib3), которая встроена в библиотеку Requests (это значит, что вам не нужно устанавливать ее отдельно). Вы можете получить ее с помощью pip:
$ pip install requests
В документации к Requests (http://docs.python-requests.org/en/latest/index.html) более подробно описывается все то, что мы будем рассматривать далее.
Практически все — от Бюро переписи населения США до Национальной библиотеки Нидерландов — имеют API; его вы можете использовать для получения данных, которыми они хотят поделиться. Некоторые из этих API, вроде Twitter и Facebook, позволяют вам (или приложениям, которые вы используете) модифицировать эти данные. Возможно, вы слышали о термине RESTful API. REST расшифровывается как representational state transfer («передача состояния представления») — это парадигма, на которой основан способ проектирования HTTP 1.1, но она не является стандартом, протоколом или требованием. Однако большинство поставщиков API для веб-сервисов следуют принципам проектирования RESTful.
Воспользуемся кодом для того, чтобы проиллюстрировать распространенные термины:
Метод является частью протокола HTTP. В RESTful API разработчик выбирает, какое действие предпримет сервер, и указывает его вам в документации к API. По адресу http://bit.ly/http-method-defs содержится список всех методов, самые распространенные из них GET, POST, PUT и DELETE. Зачастую «глаголы HTTP» соответствуют своим именам — получают, изменяют или удаляют данные.
Базовый URI является корнем API.
Клиенты будут указывать конкретный элемент, для которого им нужны данные.
Вы можете задать и другие типы мультимедиа.
Этот код выполнил запрос HTTP к ресурсу http://pypi.python.org/pypi/requests/json, который является бэкендом JSON для PyPI. Если вы взглянете на него в браузере, то увидите большую строку JSON. В библиотеке Requests возвращаемым значением для запроса HTTP будет объект типа Response:
>>> import requests
>>> response = requests.get('http://pypi.python.org/pypi/requests/json')
>>> type(response)
>>> response.ok
True
>>> response.text····# Эта команда возвращает весь текст ответа
>>> response.json()··# Эта команда преобразует текст ответа в словарь
PyPI вернул текст в формате JSON. Не существует правила, которое регулирует формат отправляемых данных, но во многих API используются JSON или XML.
Анализ JSON. Нотация объектов Javascript (Javascript Object Notation, JSON) полностью соответствует своему имени: используется для определения объектов в JavaScript. Библиотека Requests имеет встроенный анализатор JSON в объектах типа Response.
Библиотека json (https://docs.python.org/3/library/json.html) может анализировать JSON, расположенный в строках или файлах, и помещать его в словарь Python (или список, если это вам так удобнее). Она также преобразует словари или списки Python в строки JSON. Например, в следующей строке содержатся данные JSON:
json_string = '{"first_name": "Guido", "last_name":"van Rossum"}'
Проанализировать ее можно следующим образом:
import json
parsed_json = json.loads(json_string)
Теперь вы можете использовать эти данные как словарь:
print(parsed_json['first_name'])
"Guido"
Вы также можете преобразовать следующий словарь в JSON:
d = {
····'first_name': 'Guido',
····'last_name': 'van Rossum',
····'titles': ['BDFL', 'Developer'],
}
print(json.dumps(d))
'{"first_name": "Guido", "last_name": "van Rossum",
··"titles": ["BDFL", "Developer"]}'
Библиотека json была добавлена в Python 2.6. Если вы используете более раннюю версию Python, можете загрузить из PyPI библиотеку simplejson (https://simplejson.readthedocs.org/en/latest/).
simplejson предоставляет такой же API, как и модуль стандартной библиотеки json, но обновляется чаще, чем Python. Разработчики, работающие с более старыми версиями Python, все еще могут использовать функциональность, доступную в библиотеке json, импортировав simplejson. Вы можете выбрать simplejson как полноценную замену для json следующим образом:
import simplejson as json
После импортирования библиотеки simplejson под именем json все предыдущие примеры будут работать так же, как если бы вы использовали стандартную библиотеку json.
В стандартной библиотеке есть анализатор XML (методы parse() и fromstring() класса xml.etree.ElementTree), но this использует библиотеку Expat и создает объект ElementTree, сохраняющий структуру XML. Это значит, что требуется итерировать по нему и опрашивать потомков, дабы получить содержимое. Если нужно лишь получить данные, обратитесь к untangle или xmltodict. Вы можете установить их командой pip:
$ pip install untangle
$ pip install xmltodict
• untangle (https://github.com/stchris/untangle). Принимает документ XML и возвращает объект Python, чья структура отражает узлы и атрибуты документа. Например, такой файл XML:
····
можно загрузить следующим образом:
import untangle
obj = untangle.parse('path/to/file.xml')
можно получить имя элемента-потомка:
obj.root.child['name'] # is 'child1'
• xmltodict (http://github.com/martinblech/xmltodict). Преобразует XML в словарь. Например, такой файл XML:
··
····elements
····more elements
··
··
····element as well
··
можно загрузить в объект типа OrderedDict (из модуля collections стандартной библиотеки Python):
import xmltodict
with open('path/to/file.xml') as fd: doc = xmltodict.parse(fd.read())
можно получить доступ к элементам, атрибутам и значениям:
doc['mydocument']['@has'] # is u'an attribute'
doc['mydocument']['and']['many'] # is [u'elements', u'more elements']
doc['mydocument']['plus']['@a'] # is u'complex'
doc['mydocument']['plus']['#text'] # is u'element as well'
С помощью xmltodict можно преобразовать словарь обратно в XML, вызвав функцию unparse(). Она имеет потоковый режим, подходящий для обработки файлов, не помещающихся в память, а также поддерживает пространства имен.
Сайты не всегда предлагают данные в удобных форматах вроде CSV или JSON, но HTML представляет собой структурированные данные — здесь вступает в дело скраппинг.
Скраппинг сайтов — это использование компьютерной программы для анализа веб-страницы и сбора необходимых данных в формате, наиболее удобном для вас (при этом сохраняя их структуру).
По мере того как сайты предлагают свои API, они явно просят вас не использовать скраппинг: API открывает вам доступ только к тем данным, которыми владельцы сайта желают поделиться. Перед тем как начать скраппинг, прочтите условия использования целевого сайта и будьте законопослушным гражданином.
lxml (http://lxml.de/) — это довольно обширная библиотека, написанная для выполнения быстрого анализа документов XML и HTML. Позволяет обрабатывать некоторый объем некорректной разметки.
Загрузите ее с помощью pip:
$ pip install lxml
Используйте метод requests.get, чтобы получить веб-страницу с данными, преобразуйте их с помощью модуля html и сохраните результат в дереве:
Это реальная веб-страница, и данные, которые мы показываем, тоже реальные (вы можете посетить эту страницу в браузере).
Мы используем свойство page.content, а не page.text, поскольку метод html.fromstring() неявно ожидает получить объект типа bytes.
Теперь дерево содержит весь файл HTML и имеет удобную структуру. Мы можем пойти двумя путями: использовать XPath (http://lxml.de/xpathxslt.html) или CSSSelect (http://lxml.de/cssselect.html). Оба этих способа стандартные для указания пути с помощью дерева HTML, они определены и поддерживаются World Wide Web Consortium (W3C) и реализованы как модули в lxml. В этом примере мы используем XPath. Руководство по XPath (http://www.w3schools.com/xsl/xpath_intro.asp) поможет вам начать работу.
Существуют различные инструменты для получения XPath элементов изнутри вашего браузера вроде Firebug for Firefox или Chrome Inspector. Если используете Chrome, щелкните правой кнопкой мыши на элементе, выберите пункт меню Inspect element (Инспектировать элемент), подсветите код, снова щелкните правой кнопкой и выберите Copy XPath (Скопировать XPath).
После небольшого анализа мы видим, что данные на нашей странице содержатся в двух элементах: div (с заголовком buyer-name) и span (имеющий класс item-price):
Carson Busses
$29.95
Зная это, мы можем создать корректный запрос XPath и использовать lxml-функцию xpath, как показано в примере:
# Это создаст список покупателей:
buyers = tree.xpath('//div[@title="buyer-name"]/text()')
# Это создаст список цен
prices = tree.xpath('//span[@class="item-price"]/text()')
Посмотрим, что получилось:
>>> print('Buyers: ', buyers)
Buyers: ['Carson Busses', 'Earl E. Byrd', 'Patty Cakes',
'Derri Anne Connecticut', 'Moe Dess', 'Leda Doggslife', 'Dan Druff',
'Al Fresco', 'Ido Hoe', 'Howie Kisses', 'Len Lease', 'Phil Meup',
'Ira Pent', 'Ben D. Rules', 'Ave Sectomy', 'Gary Shattire',
'Bobbi Soks', 'Sheila Takya', 'Rose Tattoo', 'Moe Tell']
>>>
>>> print('Prices: ', prices)
Prices: ['$29.95', '$8.37', '$15.26', '$19.25', '$19.25',
'$13.99', '$31.57', '$8.49', '$14.47', '$15.86', '$11.11',
'$15.98', '$16.27', '$7.50', '$50.85', '$14.26', '$5.68',
'$15.00', '$114.07', '$10.09']
Сериализация данных — это преобразование структурированных данных в формат, который позволяет делиться ими или сохранить, при этом вы можете воссоздать объект в памяти на получающей стороне (или при чтении из хранилища). В некоторых случаях еще одной причиной сериализации данных является минимизация сериализованных данных, что в свою очередь минимизирует занятое дисковое пространство или требования к полосе пропускания.
В следующих разделах рассматриваются формат Pickle, характерный для Python, некоторые инструменты сериализации между языками, способы сжатия, предлагаемые стандартной библиотекой Python, а также протокол буфера, который может снизить количество операций копирования данных перед их передачей.
Нативный модуль сериализации данных для Python называется Pickle (https://docs.python.org/2/library/pickle.html). Рассмотрим пример его использования:
import pickle
# Пример словаря
grades = { 'Alice': 89, 'Bob': 72, 'Charles': 87 }
# Используем дампы для преобразования объекта в сериализованную строку
serial_grades = pickle.dumps(grades)
# Используем loads для десериализации строки в объект
received_grades = pickle.loads(serial_grades)
Функции, методы, классы и эфемерные объекты вроде конвейеров сериализовать нельзя.
В соответствии с документацией к Pickle «модуль pickle небезопасно использовать для ошибочных или вредоносных данных. Никогда не десериализуйте данные, полученные из недостоверных источников».
Если вы ищете модуль сериализации данных, который поддерживает несколько языков, подойдут Protobuf от Google (https://developers.google.com/protocol-buffers/docs/pythontutorial) и Avro от Apache (https://avro.apache.org/docs/1.7.6/gettingstartedpython.html).
В стандартной библиотеке имеется библиотека xdrlib (https://docs.python.org/3/library/xdrlib.html), позволяющая упаковывать и распаковывать данные в формате External Data Representation (XDR) (https://en.wikipedia.org/wiki/External_Data_Representation) от компании Sun. Этот формат не зависит от операционной системы и протокола передачи данных. Он работает на гораздо более низком уровне, нежели предыдущие варианты, и просто выполняет конкатенацию упакованных байтов, поэтому и клиент, и сервер должны знать тип и порядок упаковки. Рассмотрим пример сервера, получающего данные в формате XDR:
Данные могут иметь произвольную длину, поэтому мы добавили в начало файла упакованное беззнаковое число (4 байта), содержащее размер сообщения.
Мы должны знать заранее, что получаем данные типа unsigned int.
В этой строке считываем остальную часть сообщения…
…а в этой сбрасываем распаковщик, чтобы он начал работать с новыми данными.
Мы должны знать заранее, что получаем одну строку, а затем одно число с плавающей точкой.
Конечно, если обе стороны являлись программами Python, вы бы использовали Pickles. Но если сервер написан на каком-то другом языке, то соответствующий код клиента, отправляющего данные, выглядел бы так:
Сначала упакуем все данные, подлежащие отправке.
Далее отдельно упакуем длину сообщения…
…и добавим ее ко всему сообщению.
Стандартная библиотека Python поддерживает сжатие и декомпрессию данных с использованием алгоритмов zlib, gzip, bzip2 и lzma, а также создание архивов ZIP и TAR. Для того чтобы поместить в ZIP-архив данные, сериализованные с помощью Pickle, сделайте следующее:
import pickle
import gzip
data = "my very big object"
# Для запаковки и сериализации:
with gzip.open('spam.zip', 'wb') as my_zip:
····pickle.dump(data, my_zip)
# Для распаковки и десериализации:
with gzip.open('spam.zip', 'rb') as my_zip:
····unpickled_data = pickle.load(my_zip)
Элай Бендерски (Eli Bendersky), один из основных разработчиков Python, написал статью, посвященную вопросу снижения количества копий одних и тех же данных, хранящихся в памяти, с помощью буферов памяти (http://tinyurl.com/bendersky-buffer-protocol). Используя этот прием, вы даже можете считать данные из файла или сокета и поместить их в существующий буфер. Для получения более подробной информации обратитесь к документации для буферов протоколов и PEP 3118 (https://docs.python.org/3/c-api/buffer.html), где предлагаются улучшения, которые были реализованы в Python 3 и обратно портированы для версий Python 2.6 и выше.
Распределенные вычислительные системы выполняют задачу сообща (вроде игр, чат-комнат в Интернете или расчетов Hadoop) путем передачи информации друг другу.
В этом разделе сначала показываются самые популярные библиотеки для выполнения распространенных задач, связанных с сетью, а далее рассматривается шифрование (эта тема неотрывно следует за темой работы с сетью).
В Python коммуникация для соединенных сетей зачастую обрабатывается с помощью асинхронных инструментов или потоков, что позволяет обойти ограничение в один поток, создаваемое глобальной блокировкой интерпретатора. Все библиотеки, перечисленные в табл. 9.1, решают одну и ту же проблему — обходят GIL — с помощью разной функциональности.
Библиотека | Лицензия | Причины использовать |
---|---|---|
asyncio | Лицензия PSF | Предоставляет асинхронный цикл событий для управления коммуникацией с помощью неблокирующих сокетов и очередей, а также сопрограммами, определяемыми пользователем. Содержит асинхронные сокеты и очереди |
gevent | Лицензия MIT | Тесно связана с libev — библиотекой для асинхронного ввода/вывода, написанной на С. Предоставляет быстрый сервер WSGI, созданный на основе сервера HTTP. Содержит полезный модуль gevent.monkey (http://www.gevent.org/gevent.monkey.html), который имеет функции корректировки для стандартной библиотеки, что позволяет применять сторонние модули, использующие блокирующие сокеты |
Twisted | Лицензия MIT | Предоставляет асинхронные реализации более новых протоколов, например GPS, Internet of Connected Products (IoCP), и протокола Memcached (https://memcached.org/). В ее цикл событий интегрированы разнообразные фреймворки, управляемые событиями вроде wxPython или GTK. Имеет встроенный сервер SSH и клиентские инструменты |
PyZMQ | Лицензии LGPL (ZMQ) и BSD (часть с Python) | Позволяет настроить неблокирующие очереди сообщений, использующие API, похожий на сокеты, а также взаимодействовать с ними. Предоставляет поведения сокетов (запрос/ответ, публикация/подписка и отправка/получение), которые поддерживают распределенные вычисления. Используйте эту библиотеку, если хотите создать собственную инфраструктуру для коммуникации; в ее имени содержится буква Q, но она не похожа на RabbitMQ — ее можно использовать для того, чтобы создать что-то вроде RabbitMQ или что-то, что имеет совершенно другое поведение (в зависимости от выбранных шаблонов сокетов) |
pika | Лицензия BSD | Предоставляет легковесный клиент AMQP (протокол коммуникации) для соединения с RabbitMQ или другими брокерами сообщений. Включает в себя адаптеры, подходящие для использования в циклах событий Tornado или Twisted. Используйте ее вместе с брокером сообщений вроде RabbitMQ, если вам нужна более легковесная библиотека (без информационных панелей и других свистелок), которая позволяет отправлять содержимое внешнему брокеру сообщений вроде RabbitMQ |
Celery | Лицензия BSD | Предоставляет клиент AMQP для соединения с RabbitMQ или другими брокерами сообщений. Дает возможность сохранять состояния задач в бэкенде, который может использовать другие популярные варианты вроде соединения с базой данных с помощью SQLAlchemy, Memcached или другим способом. Имеет необязательный к использованию инструмент для веб-администрирования и наблюдения, который называется Flower. Может быть использована вместе с брокером сообщений вроде RabbitMQ в качестве моментально готовой к использованию системы-брокера сообщений |
Инструмент asyncio (https://docs.python.org/3/library/asyncio.html) был представлен в Python 3.4. Включает в себя идеи, почерпнутые у сообществ разработчиков вроде тех, что поддерживают библиотеки Twisted и gevent. Это инструмент для работы с конкуренцией, а самым частым приложением конкуренции являются сетевые сервера. В документации к asyncore (предшественнике asyncio) говорится следующее:
Существует лишь два способа заставить программу, работающую на одном процессоре, выполнять «больше одной задачи одновременно». Многопоточное программирование — самый простой и популярный способ сделать это, но существует еще один прием, который позволяет воспользоваться практически всеми преимуществами многопоточности, не задействуя на самом деле более одного потока. Применять этот прием имеет смысл, только если ваша программа ограничена по вводу/выводу. Если программа ограничена по процессору, то заранее запланированные потоки — это, возможно, именно то, что вам нужно. Однако сетевые сервера редко бывают ограниченными по процессору.
asyncio все еще находится в стандартной библиотеке Python на временной основе — ее API может измениться и потерять обратную совместимость, поэтому сильно не привыкайте.
Не вся функциональность нова — asyncore (объявлена устаревшей в Python 3.4) имеет цикл событий, асинхронные сокеты[105] и асинхронный ввод/вывод информации из файлов, а asynchat (также объявлена устаревшей в Python 3.4) имеет асинхронные очереди[106]. В asyncio добавлен один важный элемент — формализованная реализация сопрограмм. В Python это формально определяется как функция сопрограммы, то есть функция, чье описание начинается с конструкции async def, а не просто с def (если используется старый синтаксис, то применяется декоратор @asyncio.coroutine), и как объект, получаемый путем вызова функции сопрограммы (некого рода вычислений или операций ввода/вывода). Сопрограмма может обращаться к процессору и получить возможность участвовать в асинхронном цикле событий по очереди вместе с другими сопрограммами.
Множество страниц документации посвящено примерам, помогающим сообществу, поскольку такая концепция для языка новая. Она прозрачна, продуманна, и на нее определенно стоит обратить внимание. В этой интерактивной сессии мы просто хотим показать функции для цикла событий и некоторые доступные классы:
>>> import asyncio
>>>
>>> [l for l in asyncio.__all__ if 'loop' in l]
['get_event_loop_policy', 'set_event_loop_policy',
'get_event_loop', 'set_event_loop', 'new_event_loop']
>>>
>>> [t for t in asyncio.__all__ if t.endswith('Transport')]
['BaseTransport', 'ReadTransport', 'WriteTransport', 'Transport',
'DatagramTransport', 'SubprocessTransport']
>>>
>>> [p for p in asyncio.__all__ if p.endswith('Protocol')]
['BaseProtocol', 'Protocol', 'DatagramProtocol',
'SubprocessProtocol', 'StreamReaderProtocol']
>>>
>>> [q for q in asyncio.__all__ if 'Queue' in q]
['Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue',
'QueueFull', 'QueueEmpty']
gevent (http://www.gevent.org/) — это библиотека Python для работы с сетью, основанная на сопрограммах. Использует гринлеты, чтобы предоставить высокоуровневый синхронный API на базе цикла событий библиотеки libev (http://software.schmorp.de/pkg/libev.html), написанной на С. Гринлеты основаны на библиотеке greenlet (http://greenlet.readthedocs.io/en/latest/) — миниатюрные зеленые потоки (https://en.wikipedia.org/wiki/Green_threads) (или потоки уровня пользователя, по смыслу противоположные потоками, управляемым ядром), которые разработчик может свободно заморозить, переключаясь между гринлетами. Если хотите получить более подробную информацию, обратите внимание на семинар Кавьи Джоши (Kavya Joshi) A Tale of Concurrency Through Creativity in Python (http://bit.ly/kavya-joshi-seminar).
Многие пользуются gevent, поскольку она легковесна и тесно связана с лежащей в ее основе библиотекой libev, написанной на С, что повышает производительность. Если вам нравится идея интеграции асинхронного ввода/вывода и гринлетов, эта библиотека отлично вам подойдет. Установите ее с помощью pip:
$ pip install gevent
Рассмотрим пример из документации к greenlet:
>>> import gevent
>>>
>>> from gevent import socket
>>> urls = ['www.google.com', 'www.example.com', 'www.python.org']
>>> jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
>>> gevent.joinall(jobs, timeout=2)
>>> [job.value for job in jobs]
['74.125.79.106', '208.77.188.166', '82.94.164.162']
В документации содержится множество других примеров.
Twisted (http://twistedmatrix.com/trac/) — это управляемый событиями движок для работы с сетями. Он может применяться для создания приложений на основе разных сетевых протоколов, включая серверы и клиенты HTTP, а также приложений, использующих протоколы SMTP, POP3, IMAP или SSH, протоколы мгновенного обмена сообщениями, и многих других (http://twistedmatrix.com/trac/wiki/Documentation). Установите его с помощью команды pip:
$ pip install twisted
Twisted существует с 2002 года и имеет верное сообщество. Ее можно назвать Emacs среди библиотек сопрограмм: все функции встроены (поскольку функциональность должна быть асинхронной для того, чтобы вы могли работать). Возможно, наиболее полезными инструментами являются асинхронная оболочка для соединений с базой данных (расположен в twisted.enterprise.adbapi), DNS-сервер (в twisted.names), прямой доступ к пакетам (в twisted.pair) и дополнительные протоколы вроде AMP, GPS и SOCKSv4 (в twisted.protocols). Большая часть функциональности Twisted работает и в Python 3. Когда вы вызываете команду pip install в среде Python 3, вы получаете все библиотеки, портированные к этому моменту. Если вы нашли то, что вам нужно, в API (http://twistedmatrix.com/documents/current/api/moduleIndex.html), которого нет в вашей версии Twisted, вам стоит воспользоваться Python 2.7.
Для получения более подробной информации см. книгу Twisted (издательство O’Reilly) Джессики МакКеллар (Jessica McKellar) и Эйба Феттига (Abe Fettig). В дополнение к ней по адресу http://twistedmatrix.com/documents/current/core/examples/ приводится более 42 примеров использования Twisted, а в этом показываются их недавние достижения в скорости (http://speed.twistedmatrix.com/).
PyZMQ (http://zeromq.github.com/pyzmq/) — это привязка к Python для ZeroMQ (http://www.zeromq.org/). Вы можете установить ее с помощью команды pip:
$ pip install pyzmq
ØMQ (также записывается как ZeroMQ, 0MQ или ZMQ) — библиотека для обмена сообщениями, которая имеет API, похожий на API сокетов. Предназначена для использования в масштабируемых распределенных или одновременно выполняемых приложениях. По сути, она реализует асинхронные сокеты и очереди, а также предоставляет пользовательский список «типов» сокетов, которые определяют, как работает ввод/вывод для каждого сокета. Рассмотрим пример:
Тип сокета zmq.REP соответствует ее парадигме «запрос-ответ».
Как и в случае обычных сокетов, вы привязываете сервер к IP-адресу и порту.
Клиент имеет тип zmq.REQ — ZMQ определяет следующие константы: zmq.REQ, zmq.REP, zmq.PUB, zmq.SUB, zmq.PUSH, zmq.PULL, zmq.PAIR. Они устанавливают порядок отправки и принятия данных сокетом.
Как и обычно, клиент соединяется с IP-адресом и портом, привязанным к серверу.
Эта реализация выглядит и «крякает» как сокеты, улучшенные с помощью добавления очередей и разнообразных шаблонов ввода/вывода.
Идея использования шаблонов заключается в том, чтобы предоставить строительный материал для распределенной сети.
Сокеты имеют следующие основные шаблоны.
• Запрос — ответ. zmq.REQ и zmq.REP соединяют набор клиентов с набором сервисов. Это может использоваться для создания шаблонов удаленного вызова процедуры или распределения задач.
• Публикация — подписка. zmq.PUB и zmq.SUB соединяют набор публикаторов с набором подписчиков. Этот шаблон предназначен для распространения данных (один узел распространяет данные другим), также с его помощью можно создать дерево распределения.
• Отправка — получение (или конвейер). zmq.PUSH и zmq.PULL соединяют узлы с помощью шаблона разветвления на входе и выходе, который может иметь несколько шагов, а также циклы. С помощью этого шаблона реализуется параллельное распределение и сбор задач.
Одно из основных преимуществ ZeroMQ перед ориентированным на работу с сообщениями промежуточным ПО — библиотеку можно использовать для размещения сообщений в очереди без привлечения выделенного брокера сообщений. В документации к PyZMQ (http://pyzmq.readthedocs.io/) указаны новые возможности вроде туннелирования с помощью SSH. Остальную часть документации к ZeroMQ API лучше искать в основном руководстве к ZeroMQ (http://zguide.zeromq.org/page: all).
RabbitMQ (http://www.rabbitmq.com/) — это брокер сообщений с открытым исходным кодом, реализующий протокол Advanced Message Queuing Protocol (AMQP). Брокер сообщений — промежуточная программа, которая получает сообщения с отправляющей стороны и пересылает их получателям в соответствии с протоколом. Любой клиент, который реализует AMQP, может связываться с RabbitMQ. Для того чтобы получить RabbitMQ, перейдите на его страницу загрузки (https://www.rabbitmq.com/download.html) и следуйте инструкциям для вашей операционной системы.
Клиентские библиотеки, взаимодействующие с брокером, доступны для всех крупных языков программирования. Основными двумя библиотеками для Python являются pika и Celery — их можно установить с помощью pip:
$ pip install pika
$ pip install celery
• pika (https://pypi.python.org/pypi/pika). Это легковесный клиент AMQP 0-9-1, написанный на чистом Python. Считается предпочтительным для RabbitMQ. В руководствах по RabbitMQ (https://www.rabbitmq.com/getstarted.html) используется именно pika. Кроме того, целая страница https://pika.readthedocs.io/en/0.10.0/examples.html посвящена примерам работы с pika. Мы рекомендуем поработать с pika, как только вы установите RabbitMQ, независимо от того, какую библиотеку вы в итоге решите использовать, поскольку она довольно прямолинейна без дополнительной функциональности (это делает ее концепт довольно понятным).
• Celery (https://pypi.python.org/pypi/celery). Это клиент AMQP, который имеет гораздо больше функциональности: может использовать в качестве брокера сообщений RabbitMQ или Redis (распределенное хранилище данных в оперативной памяти), а также отслеживать задачи и результаты (и опционально сохранять их в выбранном пользователем бэкенде). Этот клиент имеет инструмент Flower (https://pypi.python.org/pypi/flower) для веб-администрирования/наблюдения за задачами. Он популярен среди веб-разработчиков. Существуют интеграционные пакеты для Django, Pyramid, Pylons, web2py и Tornado (для Flask такой пакет не нужен). Начинать работу лучше с руководства к Celery (http://tinyurl.com/celery-first-steps).
В 2013 году сформировалась Python Cryptographic Authority (PyCA) (https://github.com/pyca) — группа разработчиков, заинтересованных в создании высококачественных библиотек для шифрования[107]. Они предоставляют инструменты для шифрования и дешифрования сообщений на основе соответствующих ключей, а также криптографические хэш-функции, предназначенные для необратимого, но постоянного обфусцирования паролей или других секретных данных.
За исключением pyCrypto все библиотеки, представленные в табл. 9.2, поддерживаются PyCA. Практически все они созданы на основе библиотеки OpenSSL (https://www.openssl.org/), написанной на С (кроме тех, где это указано).
Реализация | Лицензия | Причины использовать |
---|---|---|
ssl и hashlib (а в Python 3.6 еще и secrets) | Лицензия Python Software Foundation | Hashlib предоставляет неплохой алгоритм хэширования паролей, обновляемый по мере выхода новых версий Python, а ssl предоставляет клиент SSL/TLS (и сервер, но для него, возможно, обновления будут не самыми свежими). Secrets — это генератор случайных чисел. Подходит для использования в криптографических целях |
pyOpenSSL | Лицензия Apache v2.0 | Использует самую последнюю версию OpenSSL в Python и предоставляет функции OpenSSL, недоступные в модуле стандартной библиотеки ssl |
PyNaCl | Лицензия Apache v2.0 | Содержит привязки к Python для libsodium* |
libnacl | Лицензия Apache | Представляет собой интерфейс Python для libsodium для тех, кто использует стек Salt (http://saltstack.com/) |
cryptography | Лицензия Apache v2.0 или BSD | Предоставляет прямой доступ к криптографическим примитивам, созданным на основе OpenSSL. Большинство пользователей предпочитают более высокоуровневый pyOpenSSL |
pyCrypto | Открытый доступ | Эта библиотека старше остальных, она создана на основе собственной библиотеки, написанной на C. Раньше была частью наиболее популярной библиотеки для работы с шифрованием для Python |
bcrypt | Лицензия Apache v2.0 | Предоставляет хэш-функцию bcrypt**. Полезна для тех, кто хочет использовать py-bcrypt (или работал с ней ранее) |
* libsodium (https://download.libsodium.org/doc/) — это версия библиотеки Networking and Cryptography (NaCl, произносится salt — соль); ее философия заключается в том, чтобы следовать определенным алгоритмам, которые имеют высокую производительность и которые легко использовать.
** Библиотека на самом деле содержит исходный код, написанный на С, и выполняет его сборку во время установки с помощью интерфейса C Fast Function, который мы описывали ранее. Bcrypt (https://en.wikipedia.org/wiki/Bcrypt) основан на алгоритме шифрования Blowfish.
В следующих разделах приведена дополнительная информация о библиотеках из табл. 9.2.
Модуль ssl (https://docs.python.org/3/library/ssl.html) стандартной библиотеки Python предоставляет API для сокетов (ssl.socket), который ведет себя как обычный сокет, обернутый в протокол SSL, а также ssl.SSLContext, содержащий конфигурации для соединения по SSL. http (или httplib в Python 2) использует его для поддержки HTTPS. Если вы работаете с Python 3.5, можете задействовать memory BIO (https://docs.python.org/3/whatsnew/3.5.html#ssl) — сокет будет записывать входную/выходную информацию в буфер, а не в место назначения, что позволяет, например, кодировать/декодировать данные в шестнадцатеричный формат перед записью или при чтении.
Основная часть улучшений безопасности появилась в Python 3.4 — вы можете узнать больше из заметок о выпуске (https://docs.python.org/3.4/whatsnew/3.4.html) (появилась поддержка транспортных протоколов и алгоритмов хэширования). Это оказалось настолько важно, что функциональность была портирована в Python 2.7 (описывается в PEP 466 и PEP 476). Вы можете узнать больше сведений из речи Бенджамина Питерсона (Benjamin Peterson) о состоянии ssl в Python (http://bit.ly/peterson-talk).
Если вы работаете с Python 2.7, убедитесь, что у вас установлена версия 2.7.9 или выше или что для вашей версии используется PEP 476, — по умолчанию клиенты HTTP будут выполнять проверку сертификата при соединении с помощью протокола https. Либо всегда используйте библиотеку Requests (является вариантом по умолчанию).
Команда разработчиков Python рекомендует использовать значения по умолчанию для SSL, если ваша политика безопасности не предъявляет особых требований к клиенту. В этом примере показывается безопасный почтовый клиент (вы можете найти этот код в документации в библиотеке ssl по адресу http://bit.ly/ssl-security-consider (раздел Security considerations)):
>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')
Для того чтобы убедиться в том, что сообщение не было повреждено во время передачи, используйте модуль hmac, который реализует алгоритм Keyed-Hashing for Message Authentication (HMAC), описанный RFC 2104 (https://tools.ietf.org/html/rfc2104.html). Он работает с сообщениями, хэшированными с помощью любого алгоритма из множества hashlib.algorithms_available. Для получения более подробной информации обратитесь к примеру из статьи Python Module of the Week (https://pymotw.com/2/hmac/). Если модуль у вас установлен, метод hmac.compare_digest() позволяет выполнять любые криптографические алгоритмы за константное время, чтобы защититься от атак по времени (атакующая сторона пытается определить ваш алгоритм шифрования на основе времени, которое требуется для выполнения криптографических алгоритмов).
Модуль hashlib может использоваться при генерации хэшированных паролей для безопасного хранилища или контрольных сумм с целью подтверждения сохранности данных во время передачи. Функция Password-Based Key Derivation Function 2 (PBKDF2), рекомендованная в NIST Special Publication 800–132 (http://bit.ly/nist-recommendation), в данный момент считается лучшим способом хэширования пароля. Рассмотрим пример использования этой функции вместе с salt[108]. При генерации хэшированного пароля используется 10 000 итераций алгоритма Secure Hash Algorithm для 256-битного хэша (SHA-256) (доступные алгоритмы хэширования и переменное количество итераций позволяют программисту сбалансировать устойчивость и желаемую скорость ответа):
import os
import hashlib
def hash_password(password, salt_len=16, iterations=10000, encoding='utf-8'):
····salt = os.urandom(salt_len)
····hashed_password = hashlib.pbkdf2_hmac(
········hash_name='sha256',
········password=bytes(password, encoding),
········salt=salt,
········iterations=iterations
····)
····return salt, iterations, hashed_password
Библиотека secrets (https://docs.python.org/3.6/library/secrets.html) была предложена в PEP 506 (https://www.python.org/dev/peps/pep-0506/), она доступна с версии Python 3.6. Предоставляет функции генерации токенов для безопасности, которые подходят приложениям, а также функции восстановления пароля и создания URL, которые сложно угадать. Ее документация содержит примеры и рекомендации по управлению безопасностью на базовом уровне.
Когда вышла библиотека Cryptography, pyOpenSSL (https://pyopenssl.readthedocs.io/en/stable/) обновила свои привязки так, чтобы использовать основанные на CFFI привязки Cryptography для OpenSSL и попасть под крыло PyCA.
pyOpenSSL намеренно не является частью стандартной библиотеки Python, чтобы можно было выпускать обновления безопасности с желаемой скоростью[109] — ее строят для новых версий OpenSSL, а не для версий OpenSSL, поставляющихся с вашей операционной системой (если только вы не строите ее сами для новой версии). Как правило, если вы строите сервер, то используете pyOpenSSL (обратитесь к документации для SSL от Twisted по адресу http://twistedmatrix.com/documents/12.0.0/core/howto/ssl.html — там вы найдете пример применения pyOpenSSL).
Установите ее с помощью pip:
$ pip install pyOpenSSL
И импортируйте под именем OpenSSL. В этом примере показываются несколько доступных функций:
>>> import OpenSSL
>>>
>>> OpenSSL.crypto.get_elliptic_curve('Oakley-EC2N-3')
>>>
>>> OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD)
Команда разработчиков pyOpenSSL поддерживает код примера (https://github.com/pyca/pyopenssl/tree/master/examples), который включает в себя генерацию сертификатов, способ начать использовать SSL вместо уже соединенного сокета, а также безопасный сервер XMLRPC.
Идея, лежащая в основе libsodium (http://bit.ly/introducing-sodium) (библиотеки-бэкенда, написанной на C, для PyNaCl и libnacl), заключается в том, чтобы намеренно не давать пользователям выбор — лишь лучшие варианты из доступных в их ситуации. Она не поддерживает полностью протокол TLS; если вы хотите использовать этот протокол по максимуму, выбирайте pyOpenSSL. Если вам требуется лишь устанавливать зашифрованные соединения с другими компьютерами, которыми вы управляете, или с выбранными вами протоколами и вы не хотите работать с OpenSSL, эта библиотека отлично подойдет[110].
PyNaCl произносится py-salt («пай-солт»), а libnacl — lib-salt («либ-солт»). Они обе созданы на основе библиотеки NaCl (salt) (https://nacl.cr.yp.to/).
Мы рекомендуем использовать PyNaCl (https://pypi.python.org/pypi/PyNaCl) вместо libnacl (https://libnacl.readthedocs.io/), поскольку за ней присматривает PyCA и вам не нужно отдельно устанавливать libsodium. Библиотеки, по сути, одинаковы: PyNaCl используют привязки CFFI для библиотек, написанных на C, а libnacl — ctypes (поэтому выбор библиотеки не имеет особого значения). Установите PyNaCl с помощью pip:
$ pip install PyNaCl
В докуменатции к PyNaCl по адресу https://pynacl.readthedocs.io/en/latest/ есть и примеры.
Cryptography (https://cryptography.io/en/latest/) предоставляет рецепты и примитивы для шифрования. Поддерживает Python версий 2.6–2.7 и 3.3+, а также PyPy. PyCA рекомендует в большинстве случаев пользоваться высокоуровневым интерфейсом pyOpenSSL.
Cryptography состоит из двух уровней: рецептов и опасных материалов (hazardous materials, hazmat). Уровень рецептов предоставляет простой API для выполнения качественного симметричного шифрования, а уровень hazmat — низкоуровневые криптографические примитивы. Установите ее с помощью pip:
$ pip install cryptography
В этом примере используется высокоуровневый рецепт симметричного шифрования — единственная высокоуровневая функция этой библиотеки:
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
cipher_text = cipher_suite.encrypt(b" A really secret message.")
plain_text = cipher_suite.decrypt(cipher_text)
PyCrypto (https://www.dlitz.net/software/pycrypto/) предоставляет безопасные хэш-функции, а также разнообразные алгоритмы шифрования. Поддерживает версии Python 2.1+ и Python 3+. Поскольку код, написанный на C, является пользовательским, PyCA осторожно работайте с библиотекой (но она использовалась де-факто для решения задач, связанных с шифрованием, многие годы, поэтому вы можете встретить ее в более старом коде). Установите ее с помощью pip:
$ pip install pycrypto
Использовать ее можно так:
from Crypto.Cipher import AES
# Шифрование
encryption_suite = AES.new('This is a key123', AES.MODE_CBC,
'This is an IV456')
cipher_text = encryption_suite.encrypt("A really secret message.")
# Дешифрование
decryption_suite = AES.new('This is a key123', AES.MODE_CBC,
'This is an IV456')
plain_text = decryption_suite.decrypt(cipher_text)
Если вы хотите применять алгоритм bcrypt (https://en.wikipedia.org/wiki/Bcrypt) для ваших паролей, задействуйте эту библиотеку. Тем, кто раньше пользовался py-bcrypt, должно быть нетрудно перейти на нее, поскольку библиотеки совместимы. Установите ее с помощью pip:
pip install bcrypt
Она имеет всего две функции: bcrypt.hashpw() и bcrypt.gensalt(). Последняя позволяет выбирать количество итераций — чем больше итераций, тем медленнее работает алгоритм (по умолчанию задается их разумное количество). Рассмотрим пример:
>>> import bcrypt
>>>>
>>> password = bytes('password', 'utf-8')
>>> hashed_pw = bcrypt.hashpw(password, bcrypt.gensalt(14))
>>> hashed_pw
b'$2b$14$qAmVOCfEmHeC8Wd5BoF1W.7ny9M7CSZpOR5WPvdKFXDbkkX8rGJ.e'
Сохраняем хэшированный пароль:
>>> import binascii
>>> hexed_hashed_pw = binascii.hexlify(hashed_pw)
>>> store_password(user_id=42, password=hexed_hashed_pw)
Когда приходит время проверять пароль, используйте хэшированный пароль в качестве второго аргумента функции bcrypt.hashpw() следующим образом:
>>> hexed_hashed_pw = retieve_password(user_id=42)
>>> hashed_pw = binascii.unhexlify(hexed_hashed_pw)
>>>
>>> bcrypt.hashpw(password, hashed_pw)
b'$2b$14$qAmVOCfEmHeC8Wd5BoF1W.7ny9M7CSZpOR5WPvdKFXDbkkX8rGJ.e'
>>>
>>> bcrypt.hashpw(password, hashed_pw) == hashed_pw
True
В этой главе приводятся популярные библиотеки Python, предназначенные для работы с данными, которые могут быть численными, текстовыми, а также изображениями и аудио. Практически все эти библиотеки используются для решения уникальных задач, поэтому в этой главе мы лишь опишем их, не сравнивая друг с другом. Вы можете установить библиотеки из PyPI с помощью команды pip, если не указано иное:
$ pip install library
В табл. 10.1 кратко описываются библиотеки.
Библиотека Python | Лицензия | Причины использовать |
---|---|---|
IPython | Лицензия Apache 2.0 | Предоставляет улучшенный интерпретатор Python, имеющий историю ввода, интегрированный отладчик, а также возможность строить графики в терминале (с помощью версии с Qt) |
NumPy | Лицензия BSD 3-clause | Предоставляет многомерные массивы и инструменты линейной алгебры, оптимизированные для скорости |
SciPy | Лицензия BSD | Предоставляет функции и вспомогательные программы, связанные с инженерией и наукой (от линейной алгебры до обработки сигналов), интеграцией, поиском корня, статистическим распределением и другими темами |
Matplotlib | Лицензия BSD | Позволяет строить научные графики |
Pandas | Лицензия BSD | Предоставляет ряды и объекты DataFrame, которые можно сохранять, объединять, группировать, выполнять агрегацию, индексировать окна и создавать их подмножества — очень похоже на R Data Frame или содержимое запроса SQL |
Scikit-Learn | Лицензия BSD 3-clause | Предоставляет алгоритмы машинной обработки, включающие понижение размерности, регрессию, работу с кластерами, выбор модели, ввод недостающих данных и предварительную обработку |
Rpy2 | Лицензия GPLv2 | Предоставляет интерфейс к R, который позволяет выполнять функции этого языка изнутри Python, а также передавать данные между двумя средами |
SymPy | Лицензия BSD | Предоставляет символьную математику, включающую разложение в ряд, пределы и анализ. Стремится выглядеть как полноценная вычислительная система |
nltk | Лицензия Apache | Предоставляет полноценный натуральный тулкит, имеющий модели и данные для обучения на многих языках |
pillow / PIL | Стандартная лицензия PIL (наподобие лицензии MIT) | Предоставляет огромное количество форматов файлов, а также возможности по фильтрации изображений и прочей обработке |
cv2 | Лицензия Apache 2.0 | Предоставляет программы для машинного распознавания образов, подходящие для выполнения анализа видеороликов в реальном времени, включая заранее обученные алгоритмы определения людей и лиц |
Scikit-Image | Лицензия BSD | Предоставляет подпрограммы для обработки изображений (фильтрации, регулирования, цветоделения, определения краев, пятен, углов, сегментации и пр.) |
Практически все эти библиотеки зависят от библиотек, написанных на C, в частности от SciPy или одной из ее зависимостей NumPy. Это означает, что у вас могут возникнуть проблемы с их установкой, если вы работаете в ОС Windows. Если вы используете Python в основном для анализа научных данных и не знакомы с компилированием кода на C и FORTRAN в ОС Windows, рекомендуем выбрать Anaconda или один из других вариантов, представленных в разделе «Коммерческие дистрибутивы Python» главы 2. В противном случае всегда сначала пробуйте выполнять команду pip install, а если она даст сбой, обращайтесь к руководству по установке SciPy (https://www.scipy.org/install.html).
Python часто используется для создания высокопроизводительных научных приложений. Широко применяется в академических и научных проектах, поскольку код на нем легко писать и язык имеет высокую производительность. В Python для научных вычислений зачастую используются внешние библиотеки, обычно написанные на более быстрых языках (вроде C или FORTRAN для работы с матрицами). Основные используемые библиотеки — части «стека SciPy»: NumPy, SciPy, SymPy, Pandas, Matplotlib и IPython. Подробное знакомство с ними выходит за рамки темы этой книги. Однако вы можете найти полное введение для экосистемы научного Python в Python Scientific Lecture Notes (http://scipy-lectures.github.com/).
IPython (http://ipython.org/) — это улучшенная версия интерпретатора Python, имеющая цветной интерфейс, более подробные сообщения об ошибках и режим встраивания, который позволяет отображать графики в терминале (в версии на основе Qt). Он является ядром по умолчанию для Jupyter (рассматриваются в разделе «Jupyter Notebooks» главы 7), а также интерпретатором по умолчанию для Spyder IDE (рассматривается в подразделе «Spyder» раздела «IDE» главы 3). IPython поставляется вместе с Anaconda, описанной в разделе «Коммерческие дистрибутивы Python» главы 2.
NumPy (http://numpy.scipy.org/) является частью проекта SciPy, но она выпущена как отдельная библиотека, поэтому те, кому нужна лишь базовая функциональность, могут использовать ее, не устанавливая остальную часть SciPy.
NumPy с умом обходит проблему запуска более медленных алгоритмов в Python путем использования многомерных массивов и функций, которые работают с массивами. Любой алгоритм можно представить как функцию для массивов, что позволяет запускать алгоритмы быстро. Бэкендом выступает библиотека Automatically Tuned Linear Algebra Software (ATLAS)[111] (http://math-atlas.sourceforge.net/), а также другие низкоуровневые библиотеки, написанные на C и FORTRAN. NumPy совместима с версиями Python 2.6+ и 3.2+.
Рассмотрим пример умножения матриц с помощью array.dot(), а также «трансляции», представляющей собой поэлементное умножение, где строка или столбец повторяются для отсутствующих измерений:
>>> import numpy as np
>>>
>>> x = np.array([[1,2,3],[4,5,6]])
>>> x array([[1, 2, 3],
········[4, 5, 6]])
>>>
>>> x.dot([2,2,1])
array([9, 24])
>>>
>>> x * [[1],[0]]
array([[1, 2, 3],
·······[0, 0, 0]])
SciPy (http://scipy.org/) использует NumPy для выполнения математических функций. SciPy задействует массивы NumPy в качестве базовой структуры данных. Она поставляется с модулями для решения распространенных задач научного программирования, включая линейную алгебру, анализ, особые функции и константы, а также обработку сигналов.
Рассмотрим пример, в котором используются физические константы SciPy:
>>> import scipy.constants
>>> fahrenheit = 212
>>> scipy.constants.F2C(fahrenheit)
100.0
>>> scipy.constants.physical_constants['electron mass']
(9.10938356e-31, 'kg', 1.1e-38)
Matplotlib (http://matplotlib.sourceforge.net/) — это гибкая библиотека для сборки интерактивных 2D и 3D графиков, которые также могут быть сохранены как собранные вручную численные показатели. API во многом отражает API MATLAB (http://www.mathworks.com/products/matlab/), что упрощает переход пользователей MATLAB на Python. В галерее Matplotlib по адресу http://matplotlib.sourceforge.net/gallery.html содержится множество примеров, а также исходный код к ним, что позволяет воссоздать их самостоятельно.
Тем, кто работает со статистикой, можно взглянуть на Seaborn (https://stanford.edu/~mwaskom/software/seaborn), более новую библиотеку для работы с графикой, предназначенную для визуализации статистики. О ней рассказывается в этой статье, посвященной тому, как освоить науку о данных (http://bit.ly/data-science-python-guide).
Для того чтобы строить графики, задействуйте Bokeh (http://bokeh.pydata.org/), использующую собственные библиотеки для визуализации, или Plotly (https://plot.ly/), основанную на библиотеке D3.js (https://d3js.org/), написанной на JavaScript, однако бесплатная версия Plotly может потребовать, чтобы вы хранили свои графики на их сервере.
Pandas (http://pandas.pydata.org/) (имя основано на фразе Panel Data — «панель с данными») — это библиотека, предназначенная для манипуляций с данными. Основана на NumPy, которая предоставляет множество полезных функций для получения доступа, индексирования, объединения и группирования данных. Основная структура данных (DataFrame) похожа на структуру, которую можно найти в среде статистического ПО R (то есть гетерогенные таблицы данных — имеющие в одних столбцах строки, а в других числа — с возможностью индексирования имени, операций с временными рядами, а также автоматического выстраивания данных). С ней также можно работать как с таблицей SQL или Excel Pivot Table, используя методы вроде groupby() или функции вроде pandas.rolling_mean().
Scikit-Learn (https://pypi.python.org/pypi/scikit-learn) — это библиотека, посвященная машинному обучению, которая предоставляет способы понижения размерности, заполнение отсутствующих данных, регрессию и модели классификации, модели деревьев, кластеры, автоматическую подстройку параметров моделей, построение графиков (с помощью Matplotlib) и многое другое. Она хорошо задокументирована и поставляется с огромным количеством примеров (http://scikit-learn.org/stable/auto_examples/index.html). Scikit-Learn работает с массивами NumPy, но обычно может взаимодействовать с порциями данных от Panda без особых проблем.
Rpy2 (https://pypi.python.org/pypi/rpy2) — это привязка к Python для статистического пакета R, позволяющая выполнять функции R из кода Python и передавать данные между двумя средами. Rpy2 — это объектно-ориентированная реализация привязки Rpy (http://rpy2.bitbucket.org/).
В Python определен фреймворк абстрактных базовых классов, предназначенный для разработки численных типов — от типа Number, который является основным численным типом, до типов Integral, Rational, Real и Complex. Разработчики могут создавать подклассы для этих классов при разработке других численных типов в соответствии с инструкциями, приведенными в библиотеке numbers[112] (https://docs.python.org/3.5/library/numbers.html). Существует также класс decimal.Decimal, который отслеживает численную точность (предназначен для бухгалтерского дела и других задач, где требуется точность). Иерархия типов работает в соответствии с ожиданиями:
>>> import decimal
>>> import fractions
>>> from numbers import Complex, Real, Rational, Integral
>>>
>>> d = decimal.Decimal(1.11, decimal.Context(prec=5)) # precision
>>>
>>> for x in (3, fractions.Fraction(2,3), 2.7, complex(1,2), d):
… ····print('{:>10}'.format(str(x)[:8]),
… ········[isinstance(x, y) for y in (Complex, Real, Rational, Integral)])
…
·········3 [True, True, True, True]
·······2/3 [True, True, True, False]
·······2.7 [True, True, False, False]
····(1+2j) [True, False, False, False]
·1.1 10000 [False, False, False, False]
Экспоненциальные, тригонометрические и другие распространенные функции находятся в библиотеке math, а соответствующие функции для комплексных чисел — в библиотеке cmath. Библиотека random предоставляет псевдослучайные числа, используя в качестве основного генератора Mersenne Twister (https://en.wikipedia.org/wiki/Mersenne_Twister). На момент выхода версии Python 3.4 модуль statistics стандартной библиотеки предоставляет возможность определить среднее значение и медиану, а также квадратичное отклонение и дисперсию для выборки и совокупности.
SymPy (https://pypi.python.org/pypi/sympy) — это библиотека, которую следует использовать при работе с символьной математикой в Python. Она полностью написана на Python и имеет опциональные расширения для ускорения работы, а также построения графиков и интерактивных сессий.
Символьные функции SymPy работают с объектами SymPy, такими как символы, функции и выражения, для создания других символьных выражений, например так:
>>> import sympy as sym
>>>
>>> x = sym.Symbol('x')
>>> f = sym.exp(-x**2/2) / sym.sqrt(2 * sym.pi)
>>> f sqrt(2)*exp(-x**2/2)/(2*sqrt(pi))
Их можно интегрировать как символьно, так и численно:
>>> sym.integrate(f, x)
erf(sqrt(2)*x/2)/2
>>>
>>> sym.N(sym.integrate(f, (x, — 1, 1)))
0.68268 94921 37086
Библиотека также может брать производную, раскладывать выражения в ряды, ограничивать доступные символы действительными, коммутационными или соответствующими десятку других категорий, находить ближайшее рациональное число (с заданной точностью) для числа с плавающей точкой и многое другое.
Инструменты для работы со строками в Python — одна из причин, почему многие начинают использовать язык. Мы кратко рассмотрим основные инструменты из стандартной библиотеки Python, а затем перейдем к библиотеке, которую применяют практически все члены сообщества для анализа текста: Natural Language ToolKit (nltk) (https://pypi.python.org/pypi/nltk).
Если в языке имеются символы, которые ведут себя особенным образом, когда записаны в нижнем регистре, работать с ними поможет метод str.casefold():
>>> 'Grünwalder Straße'.upper()
'GRÜNWALDER STRASSE'
>>> 'Grünwalder Straße'.lower()
'grünwalder straße'
>>> 'Grünwalder Straße'.casefold()
'grünwalder strasse'
Библиотека Python для работы с регулярными выражениями всеобъемлющая и мощная — мы видели ее в действии в пункте «Регулярные выражения (читаемость имеет значение)» на с. 177, поэтому мы не будем рассматривать ее подробно. Отметим лишь, что документация, которую можно получить с помощью вызова help(re), достаточно информативна, так что вам не придется открывать браузер во время написания кода.
Модуль difflib из стандартной библиотеки позволяет определить разницу между строками и имеет функцию get_close_matches(), которая может помочь при опечатках, когда существует известный набор правильных ответов (например, для сообщений об ошибке на сайте, посвященном путешествиям):
>>> import difflib
>>> capitals = ('Montgomery', 'Juneau', 'Phoenix', 'Little Rock')
>>> difflib.get_close_matches('Fenix', capitals)
['Phoenix']
Natural Language ToolKit (nltk) — это инструмент для анализа текста. Изначально выпущен Стивеном Бердом (Steven Bird) и Эдвардом Лопером (Edward Loper), чтобы помочь студентам усвоить курс Берда о Natural Language Processing (NLP), преподававшийся в University of Pennsylvania в 2001 году. Со временем вырос до размеров большой библиотеки, покрывающей множество языков и содержащей алгоритмы, связанные с последними исследованиями в области. Доступен под лицензией Apache 2.0, его загружают из PyPI более 100 000 раз в месяц. Его создатели выпустили книгу Natural Language Processing with Python (издательство O’Reilly): информация изложена в виде курса, который познакомит вас с Python и NLP.
Вы можете установить nltk из командной строки с помощью pip[113]. Он полагается на библиотеку NumPy, поэтому сначала установите ее:
$ pip install numpy
$ pip install nltk
Если вы используете Windows и не можете заставить работать NumPy, установленный с помощью pip, можете попробовать выполнить инструкции, приведенные по адресу http://bit.ly/numpy-install-win, на ресурсе Stack Overflow.
Размер и область видимости библиотеки могут отпугнуть некоторых пользователей, поэтому рассмотрим небольшой пример, иллюстрирующий, насколько просто работать с этим инструментом. Для начала нам понадобится получить набор данных (http://www.nltk.org/data.html) из отдельно загружаемого набора корпусов (http://www.nltk.org/nltk_data/), включая инструменты для тегирования для нескольких языков и набора данных, на которых будут тестироваться алгоритмы. Они имеют лицензию, отличающуюся от лицензии nltk, поэтому убедитесь, что вы проверили лицензию выбранного набора данных. Если знаете название корпуса текста, который нужно загрузить (в нашем случае это Punkt tokenizer[114], который мы можем использовать для разбиения текстовых файлов на предложения или отдельные слова), можете сделать это с помощью командной строки:
$ python3 — m nltk.downloader punkt — dir=/usr/local/share/nltk_data
Или можете загрузить его в рамках интерактивной сессии — stopwords содержит список слов, из-за которых значительно увеличивается общее количество слов текста вроде the, in или and во многих языках:
>>> import nltk
>>> nltk.download('stopwords', download_dir='/usr/local/share/nltk_data')
[nltk_data] Downloading package stopwords to /usr/local/share/nltk_data…
[nltk_data] Unzipping corpora/stopwords.zip.
True
Если вы не знаете название необходимого вам корпуса, можете запустить интерактивный загрузчик из интерпретатора Python, вызвав метод nltk.download() без передачи первого аргумента:
>>> import nltk
>>> nltk.download(download_dir='/usr/local/share/nltk_data')
Далее можно загрузить самую свежую версию набора данных и запустить ее в обработку. В этом фрагменте кода мы загружаем сохраненную копию «Дзена Питона»:
Корпуса загружаются медленно, поэтому нам нужно сделать это для того, чтобы действительно загрузить корпус stopwords.
Токенизатор требует наличия обученной модели — Punkt tokenizer (используемый по умолчанию) поставляется с моделью, обученной для английского языка (также выбран по умолчанию).
Биграмма — это пара соседних слов. Мы проходим по биграммам и считаем, сколько раз они встречаются.
Ключом для функции sorted() является количество элементов, они отсортированы в обратном порядке.
Конструкция '{:>25}' выравнивает справа строку с общей длиной, равной 25 символам.
Наиболее часто встречающейся биграммой «Дзена Питона» является фраза better than («лучше, чем»).
В этот раз для того, чтобы избежать большого количества слов the и is, мы удалим stopwords.
В версиях Python 3.1 и выше для подсчета можно использовать метод collections.Counter.
В этой библиотеке еще много интересного — выделите выходные и исследуйте ее!
Библиотека SyntaxNet от Google, созданная на основе TensorFlow, предоставляет обученный анализатор для английского языка (по имени Parsey McParseface) и фреймворк для сборки других моделей, даже для других языков, если у вас будут под рукой соответствующие данные. В настоящий момент библиотека доступна только для Python 2.7; подробные инстуркции по ее загрузке и использованию вы можете найти на странице https://github.com/tensorflow/models/tree/master/syntaxnet.
Тремя наиболее популярными библиотеками для обработки изображений и выполнения действий с ними в Python являются Pillow (дружественная параллельная версия библиотеки Python Imaging Library (PIL), которая подходит для преобразования форматов и простой обработки изображений), cv2 (привязка к Python для библиотеки Open-Source Computer Vision (OpenCV), которую можно использовать для определения лиц в реальном времени, а также для реализации других продвинутых алгоритмов) и более новая Scikit-Image (предоставляет возможности по простой обработке изображений, а также примитивы вроде пятен и фигур и функциональность для обнаружения границ). В следующих разделах приведена более подробная информация о каждой из них.
Python Imaging Library (PIL) (http://www.pythonware.com/products/pil/) — одна из основных библиотек для выполнения различных действий с изображениями в Python. Последняя ее версия выпущена в 2009 году, она не была портирована на Python 3. К счастью, активно разрабатывается параллельная версия, которая называется Pillow (http://python-pillow.github.io/) (ее проще устанавливать, она работает во всех операционных системах и поддерживает Python 3).
Перед установкой Pillow вам нужно установить ее зависимости. Более подробные инструкции для своей платформы вы можете найти по адресу https://pillow.readthedocs.org/en/3.0.0/installation.html (после этого все выглядит довольно понятно):
$ pip install Pillow
Рассмотрим пример использования Pillow (для команды import from применяется имя PIL, а не Pillow):
from PIL import Image, ImageFilter
# Считываем изображение
im = Image.open('image.jpg')
# Показываем изображение
im.show()
# Применяем фильтр к изображению
im_sharp = im.filter(ImageFilter.SHARPEN)
# Сохраняем отфильтрованное изображение в новый файл
im_sharp.save('image_sharpened.jpg', 'JPEG')
# Разбиваем изображение на соответствующие bands (то есть на красный, зеленый
# и синий для RGB)
r,g,b = im_sharp.split()
# Просматриваем данные EXIF, встроенные в изображение
exif_data = im._getexif()
exif_data
Другие примеры применения библиотеки Pillow смотрите в руководстве к ней по адресу http://bit.ly/opencv-python-tutorial.
Библиотека OpenSource Computer Vision (OpenCV) (http://docs.opencv.org/3.1.0/index.html) предлагает более широкие возможности для работы с изображениями и их обработки, нежели PIL. Написана на C и C++ и концентрируется на распознавании образов машиной в реальном времени. Например, она содержит первую модель, использованную при распознавании лиц в реальном времени (уже обученная на тысячах лиц; в примере по адресу https://github.com/Itseez/opencv/blob/master/samples/python/facedetect.py показывается ее применение в коде Python), модель распознавания лиц, а также модель распознавания людей среди всего остального. Реализована на нескольких языках и распространена повсеместно.
В Python обработка изображений с помощью OpenCV реализована с использованием библиотек cv2 и NumPy. Третья версия OpenCV имеет связки для версий Python 3.4 и выше, но библиотека cv2 все еще связана с OpenCV2, которая не имеет привязки к этим версиям Python. Инструкции по установке, размещенные по адресу http://tinyurl.com/opencv3-py-tutorial, содержат подробную информацию для ОС Windows и Fedora, используется версия Python 2.7. Если вы работаете с OS X, то вы сами по себе[115]. Наконец, существует вариант установки для ОС Ubuntu с использованием Python 3 (http://tinyurl.com/opencv3-py3-ubuntu). Если процесс установки станет сложным, вы можете загрузить Anaconda; они имеют бинарные файлы cv2 для всех платформ (можете прочесть статью Up & Running: OpenCV3, Python 3, & Anaconda по адресу http://tinyurl.com/opencv3-py3-anaconda, чтобы узнать, как применять cv2 и Python 3 в Anaconda).
Рассмотрим пример использования cv2:
from cv2 import *
import numpy as np
# Считываем изображение
img = cv2.imread('testimg.jpg')
# Показываем изображение
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Применяем к изображению фильтр Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Сохраняем отфильтрованное изображение в новый файл
cv2.imwrite('graytest.jpg',gray)
В сборнике руководств к OpenCV по адресу http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_tutorials.html представлено еще больше примеров для Python.
Популярность более новой библиотеки Scikit-Image (http://scikit-image.org/) растет отчасти благодаря тому, что большая часть ее исходного кода написана на Python, также она имеет отличную документацию. У нее нет полнофункциональных алгоритмов, как cv2, которую вы все еще можете использовать для алгоритмов, работающих с видео в реальном времени, но она полезна для ученых (например, они используют функции вроде определения пятен). Кроме того, библиотека включает инструменты для стандартной обработки изображений вроде фильтрации и настройки контрастности. Например, Scikit-image использовалась для создания изображений малых лун Плутона (https://blogs.nasa.gov/pluto/2015/10/05/plutos-small-moons-nix-and-hydra/). На основной странице Scikit-Image можно найти дополнительные примеры (http://scikit-image.org/docs/dev/auto_examples/).
Мы уже упоминали сжатие ZIP и сериализацию в разделе «Сериализация данных» главы 9, поэтому в этой главе нам осталось рассмотреть только базы данных.
Глава посвящена библиотекам Python, которые взаимодействуют с реляционными базами данных. Как правило, когда речь идет о базах данных, мы думаем именно о такой их разновидности — они содержат данные, сохраненные в таблицах, мы получаем к ним доступ с помощью SQL[116].
Мы уже упоминали инструменты для JSON, XML и ZIP-файлов в главе 9, а также сериализацию и XDR, когда говорили о сериализации. Для анализа YAML мы рекомендуем PyYAML (http://pyyaml.org/wiki/PyYAML) (вы можете получить его с помощью команды pip install pyyaml). В стандартной библиотеке Python также имеются инструменты для работы с файлами CSV, *.netrc, используемыми некоторыми клиентами FTP, файлами *.plist, применяемыми в OS X, а также файлами, содержащими код на диалекте формата INI из Windows с помощью модуля configparser[117].
Существует также устойчивое хранилище, работающее по принципу «ключ-значение», доступное благодаря модулю shelve из стандартной библиотеки Python. Его бэкенд является наилучшим доступным вариантом менеджера базы данных (dbm — база данных, работающая по принципу «ключ-значение») для вашего компьютера[118]:
>>> import shelve
>>>
>>> with shelve.open('my_shelf') as s:
… ····s['d'] = {'key': 'value'}
…
>>> s = shelve.open('my_shelf', 'r')
>>> s['d']
{'key': 'value'}
Вы можете узнать, какой бэкенд используете, таким способом:
>>> import dbm
>>> dbm.whichdb('my_shelf')
'dbm.gnu'
Вы также можете загрузить реализацию GNU для dbm для Windows с сайта http://gnuwin32.sourceforge.net/packages/gdbm.htm или сначала проверить ее наличие в менеджере пакетов (brew, apt, yum), а затем попробовать установить с помощью исходного кода dbm (http://www.gnu.org.ua/software/gdbm/download.html).
API для работы с базами данных в Python (DB-API2) определяет стандартный интерфейс для доступа к базам данных. Он задокументирован в PEP 249 (https://www.python.org/dev/peps/pep-0249/), а также в более подробном введении к DB-API (http://halfcooked.com/presentations/osdc2006/python_databases.html). Практически все драйверы для баз данных в Python отвечают требованиям этого интерфейса, поэтому, если вы хотите обратиться к базе данных с помощью Python, выберите драйвер, позволяющий соединиться с базой данных, которую вы используете, например sqlite3 для базы данных SQLite, psycopg2 для Postgres и MySQL-python для MySQL[119].
Код, содержащий большое количество строк SQL, а также жестко закодированные столбцы и таблицы, быстро становится неопрятным, подвержен ошибкам, его сложно отлаживать. Библиотеки, перечисленные в табл. 11.1 (за исключением sqlite3, драйвера для SQLite), предлагают уровень абстракции базы данных (database abstraction layer, DAL), который позволяет абстрагироваться от структуры, грамматики и типов данных SQL, чтобы предоставить API.
Поскольку Python — объектно-ориентированный язык, абстракция для базы данных также может реализовать объектно-реляционное отображение (object-relational mapping, ORM), чтобы соотнести объекты Python и базу данных, а также операторы для атрибутов этих классов, которые представляют собой абстрагированную версию SQL в Python.
Все библиотеки, перечисленные в табл. 11.1 (за исключением sqlite3 и Records), предоставляют ORM, их реализации используют один из двух шаблонов[120]: Active Record (записи одновременно представляют абстрагированные данные и взаимодействуют с базой данных) и Data Mapper (один слой взаимодействует с базой данных, еще один слой представляет данные, а между ними имеется функция соотнесения, которая выполняет логику, необходимую для того, чтобы преобразовывать данные между этими слоями (по сути, выполняет логику представления SQL за пределами базы данных)).
При выполнении запросов шаблоны Active Record и Data Mapper ведут себя примерно одинаково, но, работая с Data Mapper, пользователь должен явно указывать имена таблиц, добавлять первичные ключи и создавать вспомогательные таблицы для поддержки отношений «многие-ко-многим» (например, как в чеке — один идентификатор транзакции будет связан с несколькими покупками); при использовании шаблона Active Record эти действия выполняются за кулисами.
Наиболее популярными библиотеками являются sqlite3, SqlAlchemy и Django ORM. Records находится в собственной категории — это скорее клиент SQL, который предоставляет возможность форматирования выводимой информации; оставшиеся библиотеки можно рассматривать как отдельные легковесные версии Django ORM underneath (поскольку все они используют шаблон ActiveRecord), но с разными реализациями и уникальными API.
Библиотека | Лицензия | Причины использовать |
---|---|---|
sqlite3 (драйвер, не ORM) | PSFL | Находится в стандартной библиотеке. Подходит для сайтов с низким или умеренным трафиком, для которого требуются более простые типы данных и малое количество запросов, — у него небольшая задержка, поскольку не осуществляется общения по сети. Подходит для изучения SQL или DB-API для Python, а также для прототипирования приложения, работающего с базами данных |
SQLAlchemy | Лицензия MIT | Предоставляет шаблон Data Mapper, имеющий двухуровневый API, верхний уровень похож на ORM API в других библиотеках, нижний уровень работает с таблицами и непосредственно связан с базой данных. Явно дает вам возможность контролировать (с помощью API нижнего уровня) структуру и схемы вашей базы данных; это может быть полезно, если, например, ваши базы данных администрируют не веб-разработчики. / Диалекты: SQLite, PostgreSQL, MySQL, Oracle, MS-SQL Server, Firebird и Sybase (также можно зарегистрировать собственный) |
Django ORM | Лицензия BSD | Предоставляет шаблон Active Record, который может неявно сгенерировать инфраструктуру базы данных с помощью определенных пользователем моделей в приложении. Тесно связан с Django. / Диалекты: SQLite, PostgreSQL, MySQL и Oracle; также вы можете использовать стороннюю библиотеку: SAP SQL Anywhere, IBM DB2, MS-SQL Server, Firebird или ODBC |
peewee | Лицензия MIT | Предоставляет шаблон Active Record, но он работает, поскольку таблицы, которые вы определяете в ORM, вы увидите в базе данных (плюс столбец для индексирования). / Диалекты: SQLite, MySQL и Postgres (а также ваши собственные) |
PonyORM | AGPLv3 | Предоставляет шаблон Active Record, а также интуитивный синтаксис, основанный на генераторе. В сети имеется графический редактор диаграмм «сущность — отношение» (предназначен для рисования модели данных, определяющей таблицы в базе данных, а также их отношения друг с другом), который может быть преобразован в код SQL для создания таблиц. / Диалекты: SQLite, MySQL, Postgres и Oracle (а также ваш собственный) |
SQLObject | LGPL | Одним из первых начал использовать шаблон ActiveRecord в Python. / Диалекты: SQLite, MySQL, Postgres, Firebird, Sybase, MAX DB, MS-SQL Server (а также ваш собственный) |
Records (интерфейс запросов, не ORM) | Лицензия ISC | Предоставляет простой способ запрашивать базы данных и генерирует документы отчета: SQL на входе, XLS (или JSON, или YAML, или CSV, или LaTex) на выходе. Имеет интерфейс командной строки, который может быть использован для интерактивных запросов или генерации отчетов с помощью одной строки. Использует в качестве бэкенда SQLAlchemy |
В следующих разделах предоставляется дополнительная информация о библиотеках из табл. 11.1.
SQLite — это библиотека, написанная на C. Предоставляет базу данных на базе sqlite3 (https://docs.python.org/3/library/sqlite3.html). База данных хранится как один файл, по соглашению он имеет расширение *.db. Страница when to use SQLite («Когда использовать SQLite») (https://www.sqlite.org/whentouse.html) говорит, что библиотека используется как бэкенд базы данных для сайтов, имеющих сотни тысяч посещений в день. На странице https://www.sqlite.org/lang.html также приведен список команд SQL, которые понимает SQLite. Вы можете проконсультироваться с quick SQL reference от W3Schools (http://www.w3schools.com/sql/sql_quickref.asp), чтобы узнать, как использовать эти команды. Рассмотрим пример:
import sqlite3
db = sqlite3.connect('cheese_emporium.db')
db.execute('CREATE TABLE cheese(id INTEGER, name TEXT)')
db.executemany(
····'INSERT INTO cheese VALUES (??)',
····[(1, 'red leicester'),
····(2, 'wensleydale'),
····(3, 'cheddar'),
····]
)
db.commit()
db.close()
Допустимыми типами SQLite являются NULL, INTEGER, REAL, TEXT и BLOB (bytes), также с помощью документации к sqlite3 вы можете зарегистрировать новые типы данных (например, они реализуют тип datetime.datetime, который хранится как TEXT).
SQLAlchemy (http://www.sqlalchemy.org/) — очень популярный тулкит для баз данных. Django имеет возможность переключиться с собственного ORM на SQLAchemy. Это бэкенд для мегаруководства для Flask по созданию собственного блога (http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world). Pandas использует его как SQL (http://bit.ly/pandas-sql-query).
SQLAlchemy — это единственная из перечисленных здесь библиотек, следующая шаблону Data Mapper (http://martinfowler.com/eaaCatalog/dataMapper.html) Мартина Фаулера (Martin Fowler) (вместо более популярного шаблона Active Record, http://martinfowler.com/eaaCatalog/activeRecord.html). В отличие от остальных библиотек, SQLAlchemy предоставляет не только уровень ORM, но и обобщенный API (который называется уровнем Core), предназначенный для написания кода без SQL. Слой ORM находится выше уровня Core, использующего объекты, которые непосредственно соотносятся с лежащей в его основе базой данных. Пользователь должен явно соотносить эти объекты и ORM, поэтому для начала работы потребуется написать больше кода (это может быть сложно для тех, кто только приступил к работе с реляционными базами данных — создавать объекты можно только явно).
SQLAlchemy может работать на Jython и PyPy и поддерживает версии Python от 2.5 до самой свежей. В следующих фрагментах кода показано, что нужно сделать, чтобы создать объекты с отношением «многие-ко-многим». Мы создадим три объекта на уровне ORM: Customer (Покупатель), Cheese (Сыр) и Purchase (Покупка). Один покупатель может сделать много покупок (отношение «многие-к-одному»), а в одной покупке может содержаться множество видов сыра (отношение «многие-ко-многим»). Мы приводим этот пример для того, чтобы показать несоотнесенную таблицу purchases_cheeses (ей не нужно находиться в ORM, поскольку она нужна только для связи между видами сыра и покупками).
Другие ORM создали бы эту таблицу за кулисами — в этом заключается одно из самых заметных различий между SQLAlchemy и другими библиотеками:
Декларативный базовый объект — это метакласс[121], который перехватывает создание каждой таблицы из ORM и определяет соответствующую таблицу на уровне Core.
Объекты на уровне ORM наследуют от декларативного базового объекта.
Это несоотнесенная таблица на слое Core; это не класс, он не наследуется у декларативного базового объекта, соответствует таблице purchases_cheeses в базе данных и нужен для того, чтобы предоставить соотношение «многие-ко-многим» между сырами и идентификаторами покупок.
Сравните ее с соотнесенной таблицей Cheese на уровне ORM. За кулисами таблица Cheese.__table__ создается на основном слое. Она будет соответствовать таблице базы данных cheeses.
Это отношение явно показывает отношение между соотнесенными классами Cheese и Purchase: они связаны друг с другом опосредованно с помощью вторичной таблицы purchases_cheeses (в противоположность непосредственному связыванию с помощью ForeignKey).
back_populates добавляет слушателя событий, поэтому при добавлении нового объекта типа Purchase в Cheese.purchases объект типа Cheese также появится в Purchase.cheeses.
Этот фрагмент — вторая половина реализации отношения «многие-ко-многим».
Таблицы явно созданы с помощью декларативного базового объекта:
from sqlalchemy import create_engine
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
А теперь взаимодействие, при котором используются объекты слоя ORM, выглядит так же, как и для других библиотек, имеющих ORM:
Вы должны явно вызывать метод commit() для отправки изменений в базу данных.
Объекты, состоящие в отношениях «многие-ко-многим», не добавляются во время создания (их необходимо вручную вносить уже после создания).
Рассмотрим несколько примеров запросов:
Так создается отношение «многие-ко-многим» для таблицы purchases_cheeses, которая не соотносится с высокоуровневым объектом ORM.
Этот запрос считает количество покупок каждого вида сыра.
Для того чтобы узнать больше, обратитесь к документации SQLAlchemy (http://docs.sqlalchemy.org/en/rel_1_0/).
Django ORM (https://docs.djangoproject.com/en/1.9/topics/db/) — это интерфейс, используемый Django для предоставления доступа к базе данных. Их реализация шаблона Active Record больше всего похожа реализацию шаблона ActiveRecord, написанную на Ruby on Rails.
Он тесно интегрирован с Django, поэтому вы обычно будете использовать его только при создании веб-приложения с помощью Django. Обратите внимание на руководство к Django ORM от Django Girls (http://bit.ly/django-orm-tutorial), если вы хотите отслеживать процесс сборки веб-приложения[122].
Если планируете попробовать поработать с Django ORM, не создавая веб-приложение целиком, скопируйте этот скелет проекта с GitHub, чтобы использовать только Django ORM (https://github.com/mick/django_orm_only), и следуйте приведенным инструкциям. Вы можете столкнуться с некоторыми изменениями для разных версий Django. Наш файл settings.py выглядит следующим образом:
# settings.py
DATABASES = {
····'default': {
········'ENGINE': 'django.db.backends.sqlite3',
········'NAME': 'tmp.db',
····}
}
INSTALLED_APPS = ("orm_only",)
SECRET_KEY = "A secret key may also be required."
Каждая абстрагированная таблица в Django ORM является подклассом объекта Django Model. Это выглядит так:
Отношение ForeignKey обозначает отношение «многие-к-одному» — покупатель может сделать много покупок, но покупка связана с одним покупателем. Используйте OneToOneField для создания отношения «один-к-одному».
Используйте ManyToManyField для записи отношения многие-ко-многим.
Далее нужно выполнить команду для сборки таблиц. Активизировав виртуальную среду, в командной строке перейдите в каталог, где находится файл manage.py, и введите следующий код:
(venv)$ python manage.py migrate
После создания таблиц следует добавить данные в базу. Без использования метода instance.save() данные из новой строки не попадут в базу данных:
Чтобы попасть в базу данных и чтобы вы могли ссылаться на другие объекты, объекты должны быть сохранены.
Вы должны добавлять объекты, состоящие в отношениях «многие-ко-многим», отдельно.
Создание запросов с помощью ORM в Django выглядит так:
В Django оператор для фильтрации (gt, greater than — «больше») добавляется после двойного нижнего подчеркивания к атрибуту таблицы purchase_date (Django анализирует его за кулисами).
Двойное нижнее подчеркивание после идентификатора внешнего ключа предоставит доступ к атрибуту соответствующей таблицы.
В случае если вы не видели нотации, можете поместить в скобки длинное выражение и разбить его на несколько строк для удобочитаемости.
Блок annotate набора запросов добавляет дополнительные поля к каждому результату.
Основная цель peewee (http://docs.peewee-orm.com/en/latest/) — предоставить тем, кто знает SQL, легковесный способ взаимодействия с базой данных. Что вы видите, то и получаете (вы не будете разрабатывать вручную верхний уровень, который создает абстрактную структуру таблиц за кулисами, как SQLAlchemy; библиотека также не будет волшебным образом создавать нижний уровень под вашими таблицами вроде Django ORM). Ее цель заключается в том, чтобы заполнить другую нишу, — выполнять небольшое количество задач, но работать быстро, просто и по-питонски.
Здесь нет почти ничего «волшебного» за исключением создания первичных ключей для таблиц в том случае, если этого не сделал пользователь. Вы можете создать таблицу следующим образом:
peewee хранит детали конфигурации в пространстве имен Meta, эта идея заимствована у Django.
Связываем каждый объект типа Model с базой данных.
Первичный ключ будет добавлен неявно, если вы не сделаете этого самостоятельно.
Эта строка добавляет атрибут purchases к записям Customer для упрощения доступа, но ничего не делает с таблицами.
Инициализируем данные и добавляем их в базу данных за один шаг с помощью метода create() или же сначала инициализируем их, а затем добавим (существуют настройки конфигурации для управления автоматической отправкой транзакций и вспомогательными программами для них). Это делается за один этап:
Добавьте объект (вроде cat) — и peewee будет использовать его первичный ключ.
В отношении «многие-ко-многим» нет ничего волшебного — просто добавьте новые записи вручную.
Пример запроса выглядит так:
>>> for p in Purchase.select(). where(Purchase.purchase_date > d — 1 * day):
… ····print(p.customer.name, p.purchase_date)
…
Douglas 1971-12-18
Cat 1971-12-19
>>>
>>> from peewee import fn
>>> q = (Cheese
… ····.select(Cheese.kind, fn.COUNT(Purchase.id). alias('num_purchased'))
… ····.join(PurchaseCheese)
… ····.join(Purchase)
… ····.group_by(Cheese.kind)
…)
>>> for chz in q:
… ····print(chz.kind, chz.num_purchased)
…
Camembert 2
Red Leicester 1
Вам доступна коллекция надстроек (https://peewee.readthedocs.org/en/latest/peewee/playhouse.html#playhouse), содержащая продвинутую поддержку транзакций[123], поддержку пользовательских функций, которые могут получать данные и выполнять их обработку до помещения в хранилище (например, сжатие или хэширование).
PonyORM (http://ponyorm.com/) применяет другой подход к грамматике запросов: вместо написания языка, похожего на SQL, или булевых выражений он использует синтаксис генератора Python. Также он имеет графический редактор схем, который может генерировать сущности PonyORM. Поддерживает Python версий 2.6+ и 3.3+.
Для того чтобы синтаксис оставался интуитивно понятным, Pony требует, чтобы все отношения между таблицами работали в обоих направлениях — все связанные таблицы должны явно ссылаться друг на друга, например так:
В базе данных Pony с помощью сущности Entity сохраняется состояние объекта, с ее помощью соединяются база данных и сам объект.
Pony использует стандартные типы Python для определения типа столбца — от str до datetime.datetime, в дополнение к определенным пользователем сущностям вроде Purchase, Customer и Cheese.
Здесь используется lambda: Purchase, потому что Purchase еще не определен.
orm.Set(lambda: Purchase) — первая половина определения отношения «один-ко-многим» между Customer и Purchase.
orm.Required(Customer) — вторая половина отношения «один-ко-многим» между Customer и Purchase.
Отношение orm.Set(Cheese), объединенное с orm.Set(lambda: Purchase) на шаге (3), определяет отношение «многие-ко-многим».
После того как мы определили сущности для данных, создание объекта будет выглядеть как и в других библиотеках. Сущности создаются на лету и отправляются с помощью вызова orm.commit():
camembert = Cheese(type='Camembert')
leicester = Cheese(type='Red Leicester')
cat = Customer(name='Cat')
doug = Customer(name='Douglas')
d = datetime.date(1971, 12, 18)
day = datetime.timedelta(1)
Purchase(date=(d — 1 * day), customer=doug, cheeses={camembert, leicester})
Purchase(date=d, customer=cat, cheeses={camembert})
orm.commit()
Запросы в Pony действительно выглядят так, будто написаны на чистом Python:
Так выглядит запрос, созданный с помощью синтаксиса генератора для Python.
Функция orm.count() объединяет объекты путем подсчета.
SQLObject (http://www.sqlobject.org/) (выпущен в октябре 2002 года) — самый старый ORM в нашем списке. Его реализация шаблона Active Record, а также оригинальная идея перегрузки стандартных операторов (вроде ==, <, <= и т. д.) как способа абстрагирования некоторой логики SQL в Python, которая теперь реализована почти во всех библиотеках ORM, сделали его весьма популярным.
Поддерживает множество баз данных (распространенные системы вроде MySQL, Postgres и SQLite и более экзотические вроде SAP DB, SyBase и MSSQL), но в данный момент — только Python 2.6 и Python 2.7. Его все еще активно сопровождают, но он становится менее распространенным по мере использования SQLAlchemy.
Records (https://github.com/kennethreitz/records) — это минималистичная библиотека SQL, разработанная для отправки необработанных запросов SQL в разные базы данных. Представляет собой объединенные Tablib и SQLAlchemy, для которых написали хороший API и приложение командной строки (ведет себя как клиент SQL, способный выводить YAML, XLS и другие форматы Tablib). Records не собирается заменять библиотеки ORM; обычно он используется для выполнения запросов к базе данных и создания отчетов (например, ежемесячных отчетов в виде электронной таблицы, куда сохраняются последние данные о продажах). Данные могут быть использованы в программе или импортированы в один из многих полезных форматов:
>>> import records
>>> db = records.Database('sqlite:///mydb.db')
>>>
>>> rows = db.query('SELECT * FROM cheese')
>>> print(rows.dataset)
name·········|price
-|-
red leicester|1.0
wensleydale··|2.2
>>>
>>> print(rows.export('json'))
[{"name": "red leicester", "price": 1.0}, {"name": "wensleydale", "price": 2.2}]
Records предлагает инструмент для командной строки, который экспортирует данные с помощью SQL:
$ records 'SELECT * FROM cheese' yaml — url=sqlite:///mydb.db
- {name: red leicester, price: 1.0}
- {name: wensleydale, price: 2.2}
$ records 'SELECT * FROM cheese' xlsx — url=sqlite:///mydb.db > cheeses.xlsx
Существует целая вселенная баз данных not only SQL («не только SQL») — это понятие применимо к любой базе данных, не являющейся традиционной. Если вы заглянете в PyPI, то можете запутаться, поскольку увидите несколько десятков пакетов Python со схожими именами.
Мы рекомендуем искать сведения о том, какая библиотека больше всего подходит для продукта, на основном сайте проекта для Python (например, поищите в Google «Python site: vendorname.com»). Большая часть библиотек предоставляет Python API и руководство для быстрого старта. Рассмотрим несколько примеров.
• MongoDB — это распределенное хранилище документов. Вы можете рассматривать его как гигантский словарь Python (может находиться в кластере), имеющий собственный фильтр и язык запросов. Для получения API для Python обратитесь к странице https://docs.mongodb.com/getting-started/python/.
• Cassandra — это распределенное хранилище таблиц. Предоставляет возможность быстрого поиска и может работать с широкими таблицами, но не предназначено для выполнения объединений — его функция заключается в том, чтобы иметь дубликаты представлений для данных, ключи для которых содержатся в разных столбцах. Для получения более подробной информации об API для Python обратитесь к странице http://www.planetcassandra.org/apache-cassandra-client-drivers/.
• HBase — это распределенное хранилище столбцов (в этом контексте «хранилище для столбцов» означает, что данные хранятся в виде <идентификатор строки, имя столбца, значение>, что позволяет работать с очень разреженными массивами вроде наборов данных, получаемых от ссылок from и to для сайтов Всемирной паутины). Хранилище создано на основе распределенной файловой системы Hadoop. Для получения более подробной информации об API для Python обратитесь к странице https://hbase.apache.org/supportingprojects.html.
• Druid (http://druid.io/) — это распределенное хранилище столбцов, предназначенное для сбора (и опционального объединения перед сохранением) данных о событиях (в этом контексте «хранилище столбцов» означает, что столбцы можно упорядочить и отсортировать, а затем хранилище может быть сжато для получения более высокой скорости ввода/вывода и меньшего отпечатка). По ссылке https://github.com/druid-io/pydruid вы можете найти API для Python на GitHub.
• Redis — это распределенное хранилище, размещающее в памяти данные в формате «ключ-значение». Идея в том, чтобы снизить задержку, отказавшись от выполнения операций чтения с диска/записи на диск. Например, вы можете сохранять результаты выполнения частых запросов для более быстрого поиска в Сети. По адресу http://redis.io/clients#python приводится список клиентов Python для Redis, который указывает, что предпочитаемым интерфейсом является redis-py, а по ссылке https://github.com/andymccurdy/redis-py вы можете найти страницу redis-py.
• Couchbase (http://www.couchbase.com/) — еще одно распределенное хранилище документов, его API больше похож на SQL (по сравнению с API для MongoDB, который больше похож на JavaScript). По ссылке http://developer.couchbase.com/documentation/server/current/sdks/python-2.0/introduction.html вы можете найти Python SDK для Couchbase.
• Neo4j — база данных графов, предназначенная для хранения объектов, связанных подобием графов. По ссылке http://neo4j.com/developer/python/ вы можете найти руководство по Neo4j для Python.
• LMDB (Lightning Memory-mapped Database от Symas) (https://symas.com/products/lightning-memory-mapped-database/) — база данных, хранящая данные в формате «ключ-значение» в файле, отображаемом в памяти. Это означает, что файл необязательно читать с самого начала для того, чтобы дойти до того места, где хранятся данные, поэтому его производительность равна производительности хранилища в памяти. Привязки для Python находятся в библиотеке lmdb (https://lmdb.readthedocs.io/).