Динамічно імпортувати метод у файл із рядка


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().
Kris Hardy

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

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)

Будь ласка, включіть резюме / код із даного посилання. Відповіді лише на посилання не є чудовими, оскільки вони схильні до гниття посилань.
Ще один користувач

0

Використовуйте importlib(лише 2,7+).


1
Я використовую 2.6.5. Чи можу я щось зробити from __future__?
Лакшман Прасад

5
Ні, __future__це стосується функцій мови, а не нових модулів stdlib.
ThiefMaster

1
Використовуйте вбудовування, __import__як у прикладах вище. Він працює до версії 2.5 і без ключових слів до цього.
Charles Merriam

0

Я прагну до цього (як і до ряду інших бібліотек, таких як пілони та вставки, якщо пам'ять мене правильно обслуговує), це відокремити ім'я модуля від імені функції / атрибуту, використовуючи між ними ':' . Дивіться наступний приклад:

'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

1
Часто ситуація, коли це може виникнути, полягає в тому, що ви передаєте зворотні дзвінки із фреймворку до вашого додатку за допомогою файлів конфігурації (.yaml, .ini тощо). Потім ви можете вказати, яку функцію фреймворк повинен викликати тощо, за допомогою одного рядка у вашому файлі конфігурації.
Kris Hardy

Ну, так, можливо, ви захочете вибрати це як синтаксис файлу конфігурації. Але як це пов'язано з питанням ОП? (І навіть якби я вибрав це як синтаксис мого файлу конфігурації, я б віддав перевагу аналізу його за допомогою синтаксичного аналізатора конфігураційного файлу, а не випадкових функцій 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.