Динамически импортировать метод в файл из строки

84

У меня есть строка, сказать: abc.def.ghi.jkl.myfile.mymethod. Как мне динамически импортировать mymethod?

Вот как я это сделал:

def get_method_from_file(full_path):
    if len(full_path) == 1:
        return map(__import__,[full_path[0]])[0]
    return getattr(get_method_from_file(full_path[:-1]),full_path[-1])


if __name__=='__main__':
    print get_method_from_file('abc.def.ghi.jkl.myfile.mymethod'.split('.'))

Мне интересно, требуется ли вообще импорт отдельных модулей.

Изменить: я использую Python версии 2.6.5.

Лакшман Прасад
источник

Ответы:

111

Из Python 2.7 вы можете использовать функцию importlib.import_module () . Вы можете импортировать модуль и получить доступ к объекту, определенному в нем, с помощью следующего кода:

from importlib import import_module

p, m = name.rsplit('.', 1)

mod = import_module(p)
met = getattr(mod, m)

met()
от
источник
2
Стоит отметить, что в документации Python рекомендуется использовать importlib.import_module()over __import__(): docs.python.org/2/library/functions.html#__import__ - для 2.7+.
BoltzmannBrain
4
import_module + rsplit = Единственный верный путь.
Сесил Карри,
32

Вам не нужно импортировать отдельные модули. Достаточно импортировать модуль, из которого вы хотите импортировать имя, и указать fromlistаргумент:

def import_from(module, name):
    module = __import__(module, fromlist=[name])
    return getattr(module, name)

Для вашего примера abc.def.ghi.jkl.myfile.mymethodвызовите эту функцию как

import_from("abc.def.ghi.jkl.myfile", "mymethod")

(Обратите внимание, что в Python функции уровня модуля называются функциями, а не методами.)

Для такой простой задачи нет преимущества в использовании importlibмодуля.

Свен Марнах
источник
import_from () указал мне правильное направление. myClass = getattr(__import__("module.to.import", fromlist=["myClassName"]), "myClassName"). Спасибо за помощь!
Fydo
1
__import__Подход делает работу. Но попробуйте бежать help(__import__). В нем говорится: «Поскольку эта функция предназначена для использования интерпретатором Python, а не для общего использования, ее лучше использовать importlib.import_module()для программного импорта модуля».
twasbrillig
5
@twasbrillig: Когда я ответил на этот вопрос, Python 2.5 и 2.6 все еще широко использовались. Сегодня, безусловно, лучше использовать importlibвместо этого.
Sven Marnach
1
Хорошо, спасибо. Возможно, стоит обновить ответ, чтобы отразить это.
twasbrillig
26

Для Python <2.7 можно использовать встроенный метод __ import__ :

__import__('abc.def.ghi.jkl.myfile.mymethod', fromlist=[''])

Для Python> = 2.7 или 3.1 добавлен удобный метод importlib.import_module . Просто импортируйте свой модуль следующим образом:

importlib.import_module('abc.def.ghi.jkl.myfile.mymethod')

Обновление : обновленная версия в соответствии с комментариями ( я должен признать, что не прочитал импортируемую строку до конца, и я пропустил тот факт, что должен быть импортирован метод модуля, а не сам модуль) :

Python <2.7:

mymethod = getattr(__import__("abc.def.ghi.jkl.myfile", fromlist=["mymethod"]))

Python> = 2.7:

mymethod = getattr(importlib.import_module("abc.def.ghi.jkl.myfile"), "mymethod")
гекко
источник
6
Ни одно из этих решений не будет работать, потому что первым параметром должно быть имя модуля в обоих __import__()и importlib.import_module().
Крис Харди,
1
importlib.import_module ('abc.def.ghi.jkl.myfile.mymethod') не будет работать, потому что вы должны указать «какой модуль импортировать в абсолютном или относительном выражении», а не «квалифицированное» имя метода.
FRM
Очевидно, это не работает, поскольку первым импортируемым параметром должен быть модуль, а не строка.
Лакшман Прасад
11

Непонятно, что вы пытаетесь сделать со своим локальным пространством имен. Полагаю, вы хотите, my_methodкак местный житель, печатать output = my_method()?

# This is equivalent to "from a.b.myfile import my_method"
the_module = importlib.import_module("a.b.myfile")
same_module = __import__("a.b.myfile")
# import_module() and __input__() only return modules
my_method = getattr(the_module, "my_method")

# or, more concisely,
my_method = getattr(__import__("a.b.myfile"), "my_method")
output = my_method()

Хотя вы только добавляете my_methodв локальное пространство имен, вы загружаете цепочку модулей. Вы можете просмотреть изменения, просмотрев ключи sys.modulesдо и после импорта. Я надеюсь, что это яснее и точнее, чем другие ваши ответы.

Для полноты, вот как вы добавляете всю цепочку.

# This is equivalent to "import a.b.myfile"
a = __import__("a.b.myfile")
also_a = importlib.import_module("a.b.myfile")
output = a.b.myfile.my_method()

# This is equivalent to "from a.b import myfile"
myfile = __import__("a.b.myfile", fromlist="a.b")
also_myfile = importlib.import_module("a.b.myfile", "a.b")
output = myfile.my_method()

И, наконец, если вы используете __import__()и изменили свой путь поиска после запуска программы, вам может потребоваться использовать __import__(normal args, globals=globals(), locals=locals()). Почему - сложное обсуждение.

Чарльз Мерриам
источник
Ваш первый пример the_moduleи same_moduleне так , и дают разные результаты. Голосование против.
Sleepycal
7
from importlib import import_module

name = "file.py".strip('.py')
# if Path like : "path/python/file.py" 
# use name.replaces("/",".")

imp = import_module(name)

# get Class From File.py
model = getattr(imp, "classNameImportFromFile")

NClass = model() # Class From file 
Алаа Акиэль
источник
2
Спасибо за ответ, всего одна небольшая проблема: я не думаю, что .strip()ведет себя правильно в том случае, о котором вы заявили. В частности, если файл начинается с «py», эти символы также будут удалены. Например, "pyfile.py".strip(".py")производит там "file", где было бы предпочтительнее производить "pyfile"в этом случае. name.replace(".py","")хотя работает нормально.
jxmorris12 05
1

У этого веб-сайта есть хорошее решение: load_class . Я использую это так:

foo = load_class(package.subpackage.FooClass)()
type(foo) # returns FooClass

По запросу, вот код из веб-ссылки:

import importlib

def load_class(full_class_string):
    """
    dynamically load a class from a string
    """

    class_data = full_class_string.split(".")
    module_path = ".".join(class_data[:-1])
    class_str = class_data[-1]

    module = importlib.import_module(module_path)
    # Finally, we retrieve the Class
    return getattr(module, class_str)
Kingaj
источник
Пожалуйста, включите сводку / код из данной ссылки. Ответы только на ссылки не очень хороши, потому что они склонны к гниению ссылок.
Еще один пользователь
0

Используйте importlib(только 2.7+).

Дэниел Розман
источник
1
Пользуюсь 2.6.5. Могу я что-нибудь сделать from __future__?
Лакшман Прасад
5
Нет, __future__это для языковых функций, а не для новых модулей stdlib.
ThiefMaster 09
1
Используйте встроенный, __import__как в примерах выше. Он работает до версии 2.5 и без ключевых слов до этого.
Чарльз Мерриам
0

Способ, которым я склоняюсь к этому (а также ряд других библиотек, таких как pylons и paste, если моя память мне не изменяет), состоит в том, чтобы отделить имя модуля от имени функции / атрибута, используя ':' между ними . См. Следующий пример:

'abc.def.ghi.jkl.myfile:mymethod'

Это упрощает использование import_from(path)приведенной ниже функции.

def import_from(path):
    """
    Import an attribute, function or class from a module.
    :attr path: A path descriptor in the form of 'pkg.module.submodule:attribute'
    :type path: str
    """
    path_parts = path.split(':')
    if len(path_parts) < 2:
        raise ImportError("path must be in the form of pkg.module.submodule:attribute")
    module = __import__(path_parts[0], fromlist=path_parts[1])
    return getattr(module, path_parts[1])


if __name__=='__main__':
    func = import_from('a.b.c.d.myfile:mymethod')
    func()
Крис Харди
источник
1
Зачем кому-то это делать? Это требует, чтобы вызывающая сторона объединила имя модуля и имя атрибута с двоеточием, а функция должна снова разделить двоеточие. Почему бы просто не использовать сразу два параметра?
Sven Marnach 09
1
Часто такая ситуация возникает, если вы подключаете обратные вызовы из фреймворка к вашему приложению с помощью файлов конфигурации (.yaml, .ini и т. Д.). Затем вы можете указать, какую функцию должна вызывать платформа, и т. Д. С помощью одной строки в вашем файле конфигурации.
Крис Харди,
Что ж, да, вы можете выбрать это как синтаксис файла конфигурации. Но как это связано с вопросом ОП? (И даже если бы я выбрал это в качестве синтаксиса файла конфигурации, я бы предпочел, чтобы он анализировался парсером моего файла конфигурации, а не случайными функциями API.)
Свен Марнах,
-1

Как насчет этого :

def import_module(name):

    mod = __import__(name)
    for s in name.split('.')[1:]:
        mod = getattr(mod, s)
    return mod
Аймен Алсаади
источник
Использование магического метода не рекомендуется. Вместо этого используйте importlib.
dephinera
Не могли бы вы объяснить, почему это обескураживает? @dephinera
Aymen Alsaadi
Потому что магические методы предназначены для вызова интерпретатором, а не явно нашим кодом. Вызывая их явно, мы надеемся, что их подпись никогда не изменится, однако она может измениться в будущих языковых версиях.
dephinera 02 нояб.202020,