Как динамически загрузить класс Python - PullRequest
132 голосов
/ 14 февраля 2009

Учитывая строку класса Python, например, my_package.my_module.MyClass, как лучше загрузить его?

Другими словами, я ищу эквивалентную Class.forName() в Java функцию в Python. Он должен работать на Google App Engine.

Предпочтительно это будет функция, которая принимает FQN класса в виде строки и возвращает ссылку на класс:

my_class = load_class('my_package.my_module.MyClass')
my_instance = my_class()

Ответы [ 10 ]

168 голосов
/ 14 февраля 2009

Из документации по питону, вот функция, которую вы хотите:

def my_import(name):
    components = name.split('.')
    mod = __import__(components[0])
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

Причина, по которой простой __import__ не сработает, заключается в том, что любой импорт чего-либо после первой точки в строке пакета является атрибутом импортируемого вами модуля. Таким образом, что-то вроде этого не будет работать:

__import__('foo.bar.baz.qux')

Вы должны вызвать вышеуказанную функцию следующим образом:

my_import('foo.bar.baz.qux')

Или в случае вашего примера:

klass = my_import('my_package.my_module.my_class')
some_object = klass()

РЕДАКТИРОВАТЬ : Я был немного от этого. То, что вы в основном хотите сделать, это:

from my_package.my_module import my_class

Вышеуказанная функция необходима, только если у вас есть пусто из списка. Таким образом, соответствующий вызов будет выглядеть так:

mod = __import__('my_package.my_module', fromlist=['my_class'])
klass = getattr(mod, 'my_class')
102 голосов
/ 18 июля 2014

Если вы не хотите бросать свои собственные, в модуле pydoc доступна функция, которая делает именно это:

from pydoc import locate
my_class = locate('my_package.my_module.MyClass')

Преимущество этого подхода перед другими, перечисленными здесь, заключается в том, что locate найдет любой объект python по указанному пунктирному пути, а не просто объект непосредственно в модуле. например my_package.my_module.MyClass.attr.

Если вам интересно, каков их рецепт, вот функция:

def locate(path, forceload=0):
    """Locate an object by name or dotted path, importing as necessary."""
    parts = [part for part in split(path, '.') if part]
    module, n = None, 0
    while n < len(parts):
        nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
        if nextmodule: module, n = nextmodule, n + 1
        else: break
    if module:
        object = module
    else:
        object = __builtin__
    for part in parts[n:]:
        try:
            object = getattr(object, part)
        except AttributeError:
            return None
    return object

Используется функция pydoc.safeimport. Вот документы для этого:

"""Import a module; handle errors; return None if the module isn't found.

If the module *is* found but an exception occurs, it's wrapped in an
ErrorDuringImport exception and reraised.  Unlike __import__, if a
package path is specified, the module at the end of the path is returned,
not the package at the beginning.  If the optional 'forceload' argument
is 1, we reload the module from disk (unless it's a dynamic extension)."""
74 голосов
/ 07 октября 2013
import importlib

module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
29 голосов
/ 24 ноября 2011
def import_class(cl):
    d = cl.rfind(".")
    classname = cl[d+1:len(cl)]
    m = __import__(cl[0:d], globals(), locals(), [classname])
    return getattr(m, classname)
6 голосов
/ 09 апреля 2019

Если вы используете Django, вы можете использовать это. Да, я знаю, что OP не просил django, но я наткнулся на этот вопрос в поисках решения Django, не нашел его и поместил его здесь для следующего мальчика / gal, который его ищет.

# It's available for v1.7+
# https://github.com/django/django/blob/stable/1.7.x/django/utils/module_loading.py
from django.utils.module_loading import import_string

Klass = import_string('path.to.module.Klass')
func = import_string('path.to.module.func')
var = import_string('path.to.module.var')

Имейте в виду, что если вы хотите импортировать то, что не имеет ., например re или argparse, используйте:

re = __import__('re')
1 голос
/ 16 июня 2017

ОК, для меня это так (я использую Python 2.7):

a = __import__('file_to_import', globals(), locals(), ['*'], -1)
b = a.MyClass()

Тогда b является экземпляром класса 'MyClass'

0 голосов
/ 27 апреля 2019

Здесь можно поделиться тем, что я нашел на __import__ и importlib, пытаясь решить эту проблему.

Я использую Python 3.7.3.

Когда я пытаюсь попасть в класс d в модуле a.b.c,

mod = __import__('a.b.c')

Переменная mod относится к верхнему пространству имен a.

Итак, чтобы добраться до класса d, мне нужно

mod = getattr(mod, 'b') #mod is now module b
mod = getattr(mod, 'c') #mod is now module c
mod = getattr(mod, 'd') #mod is now class d

Если мы попытаемся сделать

mod = __import__('a.b.c')
d = getattr(mod, 'd')

мы на самом деле пытаемся найти a.d.

При использовании importlib я полагаю, что библиотека сделала рекурсив getattr для нас. Итак, когда мы используем importlib.import_module, мы на самом деле получаем указатель на самый глубокий модуль.

mod = importlib.import_module('a.b.c') #mod is module c
d = getattr(mod, 'd') #this is a.b.c.d
0 голосов
/ 17 декабря 2018

Если у вас уже есть экземпляр нужного вам класса, вы можете использовать функцию 'type', чтобы извлечь его тип класса, и использовать его для создания нового экземпляра:

class Something(object):
    def __init__(self, name):
        self.name = name
    def display(self):
        print(self.name)

one = Something("one")
one.display()
cls = type(one)
two = cls("two")
two.display()
0 голосов
/ 09 октября 2014

В Google App Engine есть функция webapp2, которая называется import_string. Для получения дополнительной информации см. Здесь: https://webapp -improved.appspot.com / api / webapp2.html

Итак,

import webapp2
my_class = webapp2.import_string('my_package.my_module.MyClass')

Например, это используется в webapp2.Route, где вы можете использовать обработчик или строку.

0 голосов
/ 14 февраля 2009
module = __import__("my_package/my_module")
the_class = getattr(module, "MyClass")
obj = the_class()
...