尝试基于字符串值实例化一个类并且......失败。下面的parser
对象是dict
,在示例中,我们假设有一个名为foo
,parser['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文件,并以这种方式解析,因此它们将是字符串..
答案 0 :(得分:12)
classmap = {
'foo': foo
}
obj = classmap[parser['name']]()
答案 1 :(得分:10)
答案 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
之前导入包。您真的应该构建一个映射对象,以便您可以使用此代码限制可以访问的类。