Преобразовать строку в объект класса Python? - программирование

Преобразовать строку в объект класса Python?

Учитывая, что в качестве пользователя вводится функция python, я хотел бы получить объект класса из него, если в этом определенном пространстве имен есть класс с таким именем. По сути, я хочу реализацию для функции, которая будет производить такой результат:

class Foo:
    pass

str_to_class("Foo")
==> <class __main__.Foo at 0x69ba0>

Возможно ли это?

4b9b3361

Ответ 1

Это кажется самым простым.

>>> class Foo(object):
...     pass
... 
>>> eval("Foo")
<class '__main__.Foo'>

Ответ 2

Это может сработать:

import sys

def str_to_class(str):
    return getattr(sys.modules[__name__], str)

Ответ 3

Вы можете сделать что-то вроде:

globals()[class_name]

Ответ 4

Вам нужен класс "Baz", который живет в модуле "foo.bar". С python 2.7, вы хотите использовать importlib.import_module(), так как это приведет к переходу на python 3 проще:

import importlib

def class_for_name(module_name, class_name):
    # load the module, will raise ImportError if module cannot be loaded
    m = importlib.import_module(module_name)
    # get the class, will raise AttributeError if class cannot be found
    c = getattr(m, class_name)
    return c

С python < 2,7:

def class_for_name(module_name, class_name):
    # load the module, will raise ImportError if module cannot be loaded
    m = __import__(module_name, globals(), locals(), class_name)
    # get the class, will raise AttributeError if class cannot be found
    c = getattr(m, class_name)
    return c

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

loaded_class = class_for_name('foo.bar', 'Baz')

Ответ 5

import sys
import types

def str_to_class(field):
    try:
        identifier = getattr(sys.modules[__name__], field)
    except AttributeError:
        raise NameError("%s doesn't exist." % field)
    if isinstance(identifier, (types.ClassType, types.TypeType)):
        return identifier
    raise TypeError("%s is not a class." % field)

Это точно обрабатывает классы старого и нового стиля.

Ответ 6

Я посмотрел, как django обрабатывает этот

django.utils.module_loading имеет этот

def import_string(dotted_path):
    """
    Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import failed.
    """
    try:
        module_path, class_name = dotted_path.rsplit('.', 1)
    except ValueError:
        msg = "%s doesn't look like a module path" % dotted_path
        six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])

    module = import_module(module_path)

    try:
        return getattr(module, class_name)
    except AttributeError:
        msg = 'Module "%s" does not define a "%s" attribute/class' % (
            module_path, class_name)
        six.reraise(ImportError, ImportError(msg), sys.exc_info()[2])

Вы можете использовать его как import_string("module_path.to.all.the.way.to.your_class")

Ответ 7

Да, вы можете это сделать. Предполагая, что ваши классы существуют в глобальном пространстве имен, что-то вроде этого будет делать:

import types

class Foo:
    pass

def str_to_class(s):
    if s in globals() and isinstance(globals()[s], types.ClassType):
            return globals()[s]
    return None

str_to_class('Foo')

==> <class __main__.Foo at 0x340808cc>

Ответ 8

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

PS: Я знаю... иногда поздно... но это для всех, кто спотыкается об этом в будущем.

Ответ 9

Использование importlib работало лучше всего для меня.

import importlib

importlib.import_module('accounting.views') 

Это использует нотацию строки для модуля python, который вы хотите импортировать.

Ответ 10

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

Пример: из игры, в которой классы акторов определены в Python, и вы хотите избежать других общих классов, которые должны быть достигнуты с помощью ввода пользователем.

Другой подход (как в примере ниже) должен был бы создать целый новый класс, который содержит dict выше. Это:

  • Разрешить нескольким владельцам классов для упрощения организации (например, один для классов акторов и другой для типов звука);
  • Сделайте изменения как для держателя, так и для классов, которые будут легче проходить;
  • И вы можете использовать методы класса для добавления классов в dict. (Хотя абстракция ниже не нужна, она просто для... "иллюстрации" ).

Пример:

class ClassHolder(object):
    def __init__(self):
        self.classes = {}

    def add_class(self, c):
        self.classes[c.__name__] = c

    def __getitem__(self, n):
        return self.classes[n]

class Foo(object):
    def __init__(self):
        self.a = 0

    def bar(self):
        return self.a + 1

class Spam(Foo):
    def __init__(self):
        self.a = 2

    def bar(self):
        return self.a + 4

class SomethingDifferent(object):
    def __init__(self):
        self.a = "Hello"

    def add_world(self):
        self.a += " World"

    def add_word(self, w):
        self.a += " " + w

    def finish(self):
        self.a += "!"
        return self.a

aclasses = ClassHolder()
dclasses = ClassHolder()
aclasses.add_class(Foo)
aclasses.add_class(Spam)
dclasses.add_class(SomethingDifferent)

print aclasses
print dclasses

print "======="
print "o"
print aclasses["Foo"]
print aclasses["Spam"]
print "o"
print dclasses["SomethingDifferent"]

print "======="
g = dclasses["SomethingDifferent"]()
g.add_world()
print g.finish()

print "======="
s = []
s.append(aclasses["Foo"]())
s.append(aclasses["Spam"]())

for a in s:
    print a.a
    print a.bar()
    print "--"

print "Done experiment!"

Это возвращает меня:

<__main__.ClassHolder object at 0x02D9EEF0>
<__main__.ClassHolder object at 0x02D9EF30>
=======
o
<class '__main__.Foo'>
<class '__main__.Spam'>
o
<class '__main__.SomethingDifferent'>
=======
Hello World!
=======
0
1
--
2
6
--
Done experiment!

Другим интересным экспериментом, связанным с этим, является добавление метода, который замачивает ClassHolder, чтобы вы никогда не потеряли все классы, которые вы сделали: ^)