python动态类名

时间:2010-12-22 20:00:43

标签: python

尝试基于字符串值实例化一个类并且......失败。下面的parser对象是dict,在示例中,我们假设有一个名为fooparser['name']为'foo':

obj = parser['name']()

失败,屈服于TypeError: 'str' object is not callable。但是,因为我有:

class foo:
    def __init__(self():
        print 'Hello'

如果我做obj = foo()它可以正常工作并创建正确的对象。此外,调用obj = type(parser['name'])()不起作用。

如何解决这个问题? 更新:我真的不想使用映射系统:这些类的名称是定义INI文件,并以这种方式解析,因此它们将是字符串..

8 个答案:

答案 0 :(得分:12)

classmap = {
  'foo': foo
}

obj = classmap[parser['name']]()

答案 1 :(得分:10)

回答:
Python dynamic class names

如果您知道定义了哪个类,那么有一种更简单的方法,例如:

getattr(my_module, my_class_name)()

答案 2 :(得分:5)

不要使用字符串:

parser = {}

class foo:
    pass


parser['foo'] = foo

obj = parser['foo']()

答案 3 :(得分:2)

type(name, bases, dict)内置函数是动态构造类的正确方法 - 尤其是在给出类名的字符串时。请参阅此处的文档:http://docs.python.org/library/functions.html#type

在您的特定示例中,它可能如下所示:

>>> def init(self):
...     print 'Hello'
...
>>> Foo = type('Foo', (object,), {'__init__': init})
>>> foo = Foo()
Hello

答案 4 :(得分:2)

您可以使用存储已知类的dict的元类:

# a metaclass
class Registry(type):
    # store all the types we know
    registered = {}
    def __new__(cls, name, bases, attrs):
        # create the new type
        newtype = super(Registry, cls).__new__(cls, name, bases, attrs)
        # store it
        cls.registered[name] = newtype
        return newtype

    @classmethod
    def class_by_name(cls, name):
        # get a class from the registerd classes
        return cls.registered[name]


# arbitrary base class for every class that should be in the Register
class Registered(object):
    __metaclass__ = Registry

# some classes
class Foo(Registered):
    pass

class Bar(Foo):
    pass

 # get the class object: 
print Registry.class_by_name('Foo') # <class '__main__.Foo'>
# it can be instanciated too ofc:
print Registry.class_by_name('Bar')() # <__main__.Bar object at 0x01F0F9F0>

但不是每个人都理解元类,所以你可能想避免它们以防止任何混淆。它们对于这样的东西很有用,但从其他答案中可以看出,还有很多其他的方法可以做到。

答案 5 :(得分:2)

您可以使用inspect来创建类映射:

def get_classes():
    classes = {}
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj):
            classes[name] = obj
    return classes

然后实例化一个类

>>> classes = get_classes()
>>> c = classes['ClassName']
>>> c
<class ClassName...>

大部分时间都来自How can I get a list of all classes within current module in Python?

答案 6 :(得分:-1)

响应您的更新:执行此操作的唯一方法是使用映射系统。如果您不想使用显式映射系统,那么您可以使用Python的内置映射系统之一,尽管在我看来它不那么好看和明确。

obj = globals()[parser['name']]() 

将访问名为parser['name'] == 'foo'的全局对象。如果这恰好是一个类(或者您实际想要根据用户输入实例化的类),那么您应该好好去。否则,您将不得不围绕它构建逻辑以将您真正想要的类列入白名单。

如果类来自模块,那么您可以使用该模块的__dict__属性来达到同样的效果。

obj = somemodule.__dict__[parser['name']]()

同样的警告适用于前一种情况。使用显式映射

真的更好

答案 7 :(得分:-4)

我会像其他人说的那样使用类名的映射来对类对象。您可以使用parser[foo.__name__] = foo之类的语句对其进行初始化。如果您不想使用映射对象,则必须回退使用eval函数,如下所示:

>>> class foo:
...   pass
...
>>> klass_name = 'foo'
>>> klass_inst = eval(klass_name)
>>> klass_inst
<class __main__.foo at 0x1004b2b90>
>>> inst = klass_inst()
>>> inst
<__main__.foo instance at 0x1004d2680>
>>>

当然,如果您想使用嵌入在包中的类,则必须在执行eval之前导入包。您真的应该构建一个映射对象,以便您可以使用此代码限制可以访问的类。