[[CPUCollector]]
enabled = True
hostname_method = smart
Более сложен внутренний код. За кулисами сервер вызовет метод utils.load_collectors(), используя путь, указанный в collectors_path. Рассмотрим большую часть этой функции (мы сократили ее для удобства).
Разбиваем строку (первый вызов функции); в противном случае пути являются списками строк, содержащих пути, которые указывают места, где реализованы пользовательские подклассы класса Collector.
Здесь мы рекурсивно проходим по заданным путям, добавляя каждую папку в sys.path, чтобы далее можно было импортировать подклассы класса Collector.
Здесь выполняется рекурсия — метод load_collectors() вызывает сам себя[60].
После загрузки сборщиков из подкаталогов обновите оригинальный словарь пользовательских сборщиков, добавив туда загруженные сборщики из этих подкаталогов.
С момента введения Python 3.1 модуль importlib стандартной библиотеки Python является предпочтительным способом сделать это (с помощью модуля importlib.import_module; фрагменты importlib.import_module также были портированы в Python 2.7). Это показывает, как можно программно импортировать модуль, используя строку с его именем.
Так можно программно получить доступ к атрибутам модуля, имея лишь строку с именем атрибута.
Метод load_dynamic_class здесь можно и не использовать. Он повторно импортирует модуль, проверяет, что названный класс является классом на самом деле, проверяет, что он является подклассом класса Collector, и, если это верно, — возвращает только что загруженный класс. Избыточность часто встречается в исходном коде, который пишут большие группы людей.
Здесь они получают имя класса для дальнейшего использования при применении настроек из файла конфигурации (имея только строку, содержащую имя класса).
Примеры из стиля Diamond
В Diamond вы можете найти отличный пример использования замыкания, который демонстрирует все, о чем мы говорили в пункте «Замыкания с поздним связыванием» подраздела «Распространенные подводные камни» раздела «Стиль кода» главы 4 по поводу того, что такое поведение зачастую весьма желательно.
Пример использования замыкания (когда подводный камень вовсе не подводный камень). Замыкание — это функция, использующая переменные, доступные в локальной области видимости, которые в противном случае будут недоступны при вызове функции. В других языках их, возможно, будет трудно реализовать и понять, но это не относится к Python, поскольку в нем функции обрабатываются так же, как и любые другие объекты[61]. Например, функции могут быть переданы как аргумент, также их можно возвращать из других функций.
Рассмотрим фрагмент кода исполняемого файла diamond, который показывает, как реализовать замыкание в Python.
Когда мы пропускаем код, отсутствующие части описываем в комментарии, перед которым стоят две тильды (##~~).
Мы пользуемся файлом PID[62], чтобы убедиться, что демон уникален (то есть мы не запустили его дважды случайно), а также для быстрого сообщения с другими сценариями при передаче им связанных идентификаторов процессов. Этот файл также нужен для того, чтобы удостовериться, что процесс завершился ненормально (поскольку в этом сценарии файл PID удаляется лишь при нормальном завершении работы).
Весь этот код нужен для того, чтобы предоставить контекст, ведущий к замыканию. К этому моменту либо наш процесс запущен как демон (и теперь имеет другой PID), либо мы пропустим эту часть, поскольку правильный PID уже записан в файл.
sigint_handler() и есть замыкание. Оно определяется внутри функции main(), а не на высшем уровне, за пределами других функций, поскольку ему нужно знать, искать ли файл PID, и если да — то где.
Позволяет получить информацию из параметров командной строки, которую нельзя получить до вызова функции main(). Это означает, что все параметры, связанные с файлом PID, являются локальными переменными пространства имен функции main.
60
В Python предусмотрен лимит для рекурсии (максимальное количество раз, которое функция может вызвать сама себя) — он по умолчанию значительно ограничивает ваши возможности (существует для того, чтобы предупредить избыточное использование рекурсии). Вы можете узнать значение этого ограничения, введя import sys; sys.getrecursionlimit().
61
Язык программирования имеет функции первого класса, если он рассматривает функции как объекты первого класса. В частности, это означает, что язык поддерживает передачу функций в качестве аргументов другим функциям, возврат их как результат других функций, присваивание их переменным или сохранение в структурах данных.
62
PID расшифровывается как process identifier (идентификатор процесса). Каждый процесс имеет уникальный идентификатор, который доступен в Python благодаря модулю os в стандартной библиотеке: os.getpid().