Есть ли у Python эквивалент Java Class.forName ()? - PullRequest
87 голосов
/ 17 января 2009

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


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

Ответы [ 6 ]

153 голосов
/ 17 января 2009

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

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

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

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

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

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

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 на самом деле является классом его самого, вы можете сделать, например:

a = m() #instantiate a new instance of the class    
b = m( arg1, arg2 ) # pass arguments to the constructor
24 голосов
/ 17 января 2009

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

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)
11 голосов
/ 05 мая 2015
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 голосов
/ 31 августа 2010

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

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)
3 голосов
/ 17 января 2009

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

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

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

>>> 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
1 голос
/ 11 декабря 2013

Это находится в стандартной библиотеке 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
...