Python: Получение класса/функции по строке имени

В Django есть замечательная, по моему мнению, функциональность, позволяющая указывать view для url dispatcher‘а не импортируя ее явно, а в виде строки вроде 'mysite.views.archive', где mysite — это модуль application, views — это его подмодуль с view-объектами (функции или CBV — class based view), а archive— это собственно сам объект view. При этом url dispatcher Django сам импортирует все необходимые модули и получает ссылку на указанный в строке объект. Нужна такая-же функциональность? Как вам такой вариант:

>>> load_object('os.path.isfile')
<function isfile at 0x01361230>

Код и объяснение под катом

def load_object(object_path, module_parts_count = 1):
    import inspect
    def load_module(module_parts):
        mod = __import__( '.'.join(module_parts) )
        for module_part in module_parts[1:]:
            mod = getattr(mod, module_part)
        return mod
    parts = object_path.split('.')
    module_parts = parts[:module_parts_count]
    other_parts = parts[module_parts_count:]
    m = load_module( module_parts )
    for part in other_parts:
        if not hasattr(m,part) and inspect.ismodule(m):
            module_parts.append(part)
            m = load_module( module_parts )
        else:
            m = getattr(m, part)
    return m

Загрузка объекта состоит из двух частей: загрузка модуля и получение объекта по цепочке атрибутов. Для импорта модуля в Python есть builtin-функция __import__('package.subpackage.modulename') — которая в аргумент принимает строку, представляющую собой имя модуля (и его package, если необходимо). Эта функция выполняет загрузку поочередно всех подмодулей, указанных в строке и возвращет объект главного модуля. Его загруженные дочерние модули добавлены как атрибуты. Я написал неольшой хэлпер, выполяющий эту загрузку: load_module(list_of_submodule_names) возвращает объект конечного модуля.
Пример:

>>> load_module(['os','path'])
<module 'ntpath' from 'C:\Python27\lib\ntpath.pyc'>

После того, как модуль импортирован, все статические вызовы в нем обработаны, можно обращаться к нужному атрибуту. Сложность только в том, что строка 'os.path.isfile' содержит сразу и модуль и объект, и на начальном этапе их невозможно разделить. Для этого есть два выхода: либо явно указать сколько из элементов — модули (через параметр module_parts_count), либо в случае отсутствия необходимого атрибута у модуля импортировать его, как подмодуль. определить, является ли объект модулем, позволяет библиотека стандартная библиотека inspect, а именно функция inspect.ismodule(obj). Получить атрибут по имени можно при помощи стандартной функции getattr(obj, attr_name).

Вот, собственно, и все.
Спасибо, что читаете!

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *