Имеет ли python эквивалент Java Class.forName()?

Мне нужно принять строковый аргумент и создать объект класса, названного в этой строке в Python. В Java я использовал бы Class.forName().newInstance(). Есть ли эквивалент в Python?


Спасибо за ответы. Чтобы ответить тем, кто хочет знать, что я делаю: я хочу использовать аргумент командной строки в качестве имени класса и создать его экземпляр. Я на самом деле программирую в Jython и создавая экземпляры классов Java, а следовательно, и Java-вопрос. getattr() отлично работает. Большое спасибо.

Ответ 1

Отражение в python намного проще и гораздо более гибко, чем в Java.

Я рекомендую прочитать этот учебник

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

Один совет: не пытайтесь программировать в стиле Java, когда вы находитесь на питоне.

Если вы можете объяснить, что вы пытаетесь сделать, возможно, мы сможем помочь вам найти более питонический способ сделать это.

Здесь функция, которая делает то, что вы хотите:

def get_class( kls ):
    parts = kls.split('.')
    module = ".".join(parts[:-1])
    m = __import__( module )
    for comp in parts[1:]:
        m = getattr(m, comp)            
    return m

Вы можете использовать возвращаемое значение этой функции, как если бы это был сам класс.

Вот пример использования:

>>> D = get_class("datetime.datetime")
>>> D
<type 'datetime.datetime'>
>>> D.now()
datetime.datetime(2009, 1, 17, 2, 15, 58, 883000)
>>> a = D( 2010, 4, 22 )
>>> a
datetime.datetime(2010, 4, 22, 0, 0)
>>> 

Как это работает?

Мы используем __import__ для импорта модуля, который содержит класс, который требует, чтобы мы сначала извлекли имя модуля из полного имени. Затем мы импортируем модуль:

m = __import__( module )

В этом случае m будет ссылаться только на модуль верхнего уровня,

Например, если ваш класс живет в модуле foo.baz, тогда m будет модулем foo
Мы можем легко получить ссылку на foo.baz, используя getattr( m, 'baz' )

Чтобы перейти от модуля верхнего уровня к классу, нужно рекурсивно использовать gettatr для частей имени класса

Скажем, например, если имя класса foo.baz.bar.Model, то мы делаем следующее:

m = __import__( "foo.baz.bar" ) #m is package foo
m = getattr( m, "baz" ) #m is package baz
m = getattr( m, "bar" ) #m is module bar
m = getattr( m, "Model" ) #m is class Model

Это то, что происходит в этом цикле:

for comp in parts[1:]:
    m = getattr(m, comp)    

В конце цикла m будет ссылкой на класс. Это означает, что m на самом деле является классом itlef, вы можете сделать, например:

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor

Ответ 2

Предполагая, что класс находится в вашей области:

globals()['classname'](args, to, constructor)

В противном случае:

getattr(someModule, 'classname')(args, to, constructor)

Изменить: Обратите внимание: вы не можете дать имя, например 'foo.bar', getattr. Вам нужно разбить его. и вызовите getattr() для каждого фрагмента слева направо. Это будет обрабатывать это:

module, rest = 'foo.bar.baz'.split('.', 1)
fooBar = reduce(lambda a, b: getattr(a, b), rest.split('.'), globals()[module])
someVar = fooBar(args, to, constructor)

Ответ 3

def import_class_from_string(path):
    from importlib import import_module
    module_path, _, class_name = path.rpartition('.')
    mod = import_module(module_path)
    klass = getattr(mod, class_name)
    return klass

Использование

In [59]: raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()
---------------------------------------------------------------------------
DeadlineExceededError                     Traceback (most recent call last)
<ipython-input-59-b4e59d809b2f> in <module>()
----> 1 raise import_class_from_string('google.appengine.runtime.apiproxy_errors.DeadlineExceededError')()

DeadlineExceededError: 

Ответ 4

Еще одна реализация.

def import_class(class_string):
    """Returns class object specified by a string.

    Args:
        class_string: The string representing a class.

    Raises:
        ValueError if module part of the class is not specified.
    """
    module_name, _, class_name = class_string.rpartition('.')
    if module_name == '':
        raise ValueError('Class name must contain module part.')
    return getattr(
        __import__(module_name, globals(), locals(), [class_name], -1),
        class_name)

Ответ 5

Кажется, вы приближаетесь к этому из середины, а не к началу. Что вы на самом деле пытаетесь сделать? Поиск класса, связанного с данной строкой, является средством для завершения.

Если вы проясните свою проблему, которая может потребовать вашего собственного умственного рефакторинга, лучшее решение может показаться.

Например: пытаетесь загрузить сохраненный объект на основе его имени типа и набора параметров? Python заставляет это расплываться, и вы должны посмотреть на pickle module. И хотя процесс рассыпания делает именно то, что вы описываете, вам не нужно беспокоиться о том, как он работает внутри:

>>> class A(object):
...   def __init__(self, v):
...     self.v = v
...   def __reduce__(self):
...     return (self.__class__, (self.v,))
>>> a = A("example")
>>> import pickle
>>> b = pickle.loads(pickle.dumps(a))
>>> a.v, b.v
('example', 'example')
>>> a is b
False

Ответ 6

Это находится в стандартной библиотеке python, как unittest.TestLoader.loadTestsFromName. К сожалению, этот метод продолжает выполнять дополнительные связанные с тестированием действия, но этот первый га выглядит повторно пригодным для использования. Я отредактировал его, чтобы удалить связанные с тестированием функции:

def get_object(name):
    """Retrieve a python object, given its dotted.name."""
    parts = name.split('.')
    parts_copy = parts[:]
    while parts_copy:
        try:
            module = __import__('.'.join(parts_copy))
            break
        except ImportError:
            del parts_copy[-1]
            if not parts_copy: raise
    parts = parts[1:]

    obj = module
    for part in parts:
        parent, obj = obj, getattr(obj, part)

    return obj

Ответ 7

Мне нужно было получить объекты для всех существующих классов в my_package. Поэтому я импортирую все необходимые классы в my_package __init__.py.

Итак, моя структура каталогов выглядит следующим образом:

/my_package
    - __init__.py
    - module1.py
    - module2.py
    - module3.py

И мой __init__.py выглядит так:

from .module1 import ClassA
from .module2 import ClassB

Затем я создаю такую функцию:

def get_classes_from_module_name(module_name):
    return [_cls() for _, _cls in inspect.getmembers(__import__(module_name), inspect.isclass)]

Где module_name = 'my_package'

проверить документ: https://docs.python.org/3/library/inspect.html#inspect.getmembers