Имеет ли 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