Динамічна інстанція від імені рядка класу в динамічно імпортованому модулі?


180

У python я повинен інстанціювати певний клас, знаючи його ім'я в рядку, але цей клас "живе" в динамічно імпортному модулі. Приклад наступний:

Сценарій навантажувача:

import sys
class loader:
  def __init__(self, module_name, class_name): # both args are strings
    try:
      __import__(module_name)
      modul = sys.modules[module_name]
      instance = modul.class_name() # obviously this doesn't works, here is my main problem!
    except ImportError:
       # manage import error

сценарій динамічно завантаженого модуля:

class myName:
  # etc...

Я використовую це розташування, щоб зробити будь-який динамічно завантажений модуль, який буде використовуватися класом навантажувача, дотримуючись певних заздалегідь визначених способів поведінки в dyn-load-модулях ...

Відповіді:


251

Можна використовувати гетатр

getattr(module, class_name)

для доступу до класу. Більш повний код:

module = __import__(module_name)
class_ = getattr(module, class_name)
instance = class_()

Як було зазначено нижче , ми можемо використовувати importlib

import importlib
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
instance = class_()

3
module = __import__(module, fromlist=[name])працював тільки на мене.
умпірський

16
Якщо у когось виникають проблеми із згаданим вище методом імпорту Sven, я виявив, що мій код краще працює, використовуючи наступний метод замість importlib.import_module . Може використовуватися як: module = importlib.import_module (ім'я_модуля)
jpennell

@jpennell Ви повинні опублікувати це як відповідь, часто корисніше мати можливість безпосередньо використовувати рядок, яку повернувobj.__module__
Anentropic

importlib.import_moduleзавантажить файл .py, якщо потрібно, та обробляє повний клас module.name.pathing.to.get.to.the. __import__не зробить жодної з цих речей у середовищі джанго (не перевіряється поза цим)
Джеймс

123

тл; д-р

Імпортуйте кореневий модуль importlib.import_moduleі завантажте клас за його ім'ям за допомогою getattrфункції:

# Standard import
import importlib
# Load "module.submodule.MyClass"
MyClass = getattr(importlib.import_module("module.submodule"), "MyClass")
# Instantiate the class (pass arguments to the constructor, if needed)
instance = MyClass()

пояснення

Напевно, ви не хочете використовувати __import__динамічний імпорт модуля за назвою, оскільки він не дозволяє імпортувати підмодулі:

>>> mod = __import__("os.path")
>>> mod.join
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'join'

Ось що говорить доктор python __import__:

Примітка. Це розширена функція, яка не потрібна в повсякденному програмуванні Python, на відміну від importlib.import_module ().

Натомість використовуйте стандартний importlibмодуль для динамічного імпорту модуля за назвою. Після цього getattrви можете інстанціювати клас за його назвою:

import importlib
my_module = importlib.import_module("module.submodule")
MyClass = getattr(my_module, "MyClass")
instance = MyClass()

Ви також можете написати:

import importlib
module_name, class_name = "module.submodule.MyClass".rsplit(".", 1)
MyClass = getattr(importlib.import_module(module_name), class_name)
instance = MyClass()

Цей код дійсний у python ≥ 2.7 (включаючи python 3).


1
можливо я не розумію вашої відповіді, але я використовував імпорт для імпорту підмодулів: __import __ ("модуль". + submodule_name_string)
Хав'єр Новоа C. C.

Наступний код приводить до AttributeError: в mod = __import__("os.path"); mod.joinтой час як наступний doesnt:mod = importlib.import_module("os.path"); mod.join
Régis B.

о, я бачу, ти маєш рацію, але я зробив наступне, щоб отримати метод os.path.join: getattr (sys.modules ["os.path"], "приєднатися")
Хав'єр Новоа C. C.

га! і я бачу, що при використанні цього " імпорт " зайвий XD
Хав'єр Новоа C.

2
Параметр, позначений як відповідь, для мене не працював (використовуючи підмодулі), однак ця відповідь робить. Я думаю, що це має бути відповіддю, оскільки це більш загальне рішення для динамічного завантаження модулів.
ркачач

14

Використовуйте getattrдля отримання атрибута від імені в рядку. Іншими словами, отримайте екземпляр як

instance = getattr(modul, class_name)()

10

Копіювати та вставляти фрагмент:

import importlib
def str_to_class(module_name, class_name):
    """Return a class instance from a string reference"""
    try:
        module_ = importlib.import_module(module_name)
        try:
            class_ = getattr(module_, class_name)()
        except AttributeError:
            logging.error('Class does not exist')
    except ImportError:
        logging.error('Module does not exist')
    return class_ or None

5

Якщо ви хочете, щоб це речення from foo.bar import foo2було завантажено динамічно, вам слід це зробити

foo = __import__("foo")
bar = getattr(foo,"bar")
foo2 = getattr(bar,"foo2")

instance = foo2()

4

Можна просто використовувати pydoc.locateфункцію.

from pydoc import locate
my_class = locate("module.submodule.myclass")
instance = my_class()

2

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

def get_class(fully_qualified_path, module_name, class_name, *instantiation):
    """
    Returns an instantiated class for the given string descriptors
    :param fully_qualified_path: The path to the module eg("Utilities.Printer")
    :param module_name: The module name eg("Printer")
    :param class_name: The class name eg("ScreenPrinter")
    :param instantiation: Any fields required to instantiate the class
    :return: An instance of the class
    """
    p = __import__(fully_qualified_path)
    m = getattr(p, module_name)
    c = getattr(m, class_name)
    instance = c(*instantiation)
    return instance
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.