在Python中使用变量作为类名?

49

我想知道如何在Python中使用变量作为对象和函数名称。在PHP中,可以这样做:

$className = "MyClass";

$newObject = new $className();

在Python中如何做这种事情?或者说,我是否没有充分理解Python的一些基本差异,如果是的话,是什么?


我很想知道为什么你需要这样做--我认为可能有一种更Pythonic的方式,不需要变量类名。 - TimB
有趣,但我同意TimB的看法,我想听听这个的用例。 - monkut
3
最近我实现了一个类的两个不同子类。它们为同一种工作提供了相似但略有不同的实现,而且它们的API也差不多。我猜这就是策略模式吧?无论如何,我想根据上下文动态地使用其中一个。 - Sam McAfee
策略 = 字典(策略1=类1, 策略2=类2)。 策略"策略1" - AME
我曾经遇到一个类似的情况,在配置文件中需要找到要实例化的类的名称。@TimB 的 [eval] 使用方法是解决我的方案。 - Serge Tahé
显示剩余2条评论
6个回答

68

假设some_module有一个名为"class_name"的类:

import some_module
klass = getattr(some_module, "class_name")
some_object = klass()

我应该指出,在这里你需要小心:如果字符串来自用户,将字符串转换为代码可能是危险的,因此在这种情况下要牢记安全性。 :)

另一种方法(假设我们仍在使用“class_name”):

class_lookup = { 'class_name' : class_name }
some_object = class_lookup['class_name']()  #call the object once we've pulled it out of the dict

后一种方法可能是最安全的方法,如果可能的话,这可能就是你应该使用的方法。

非常好,我正在为动态类和动态字段创建一个句柄。我做了 classes = {'class1': Class1, 'class2': Class2,...}。现在我可以使用 myob = classes['<class_name'].objects.get(id=<obj_id>) 获取对象。 - Furbeenator
很棒的简单技巧。在发现这个之前我苦恼了2个小时。谢谢。 - Parag Tyagi

31
在Python中,
className = MyClass
newObject = className()

第一行将变量className指向与MyClass相同的东西。然后,下一行通过className变量调用MyClass构造函数。

作为一个具体的例子:

>>> className = list
>>> newObject = className()
>>> newObject
[]

(在Python中,listlist类的构造函数。)

区别在于,在PHP中,您将要引用的类的名称表示为字符串,而在Python中,您可以直接引用相同的类。 如果您必须使用字符串(例如如果类的名称是动态创建的),则需要使用其他技术。


30
因为你的解决方案与问题中的不符合而被踩了点赞。他所寻找的是使用字符串实例化一个类。 - reggie
1
请遵循PEP8规范。使用混合大小写的名称似乎违背了SO所代表的价值观。 - dcsordas
1
同意第一条评论,这不能用于存储在变量中的类名称。 - Matt
它对于提问者起作用,这就是为什么他接受了 Matt 的答案。 同时它也对我起作用。 - Mr.L
@reggie,我认为他问的是关于字符串的问题,我认为提问者也可以通过Greg Hewgill的解决方案来解决问题。因为我们想要使用的所有类都必须已经存在,他可以将它们作为名称导入以使用,而不是随机字符串! - joe-khoa

25
如果您需要在Python中创建一个动态类(即名称为变量的类),则可以使用type()函数,它有三个参数:name、bases和attrs。
>>> class_name = 'MyClass'
>>> klass = type(class_name, (object,), {'msg': 'foobarbaz'})

<class '__main__.MyClass'>

>>> inst = klass()
>>> inst.msg
foobarbaz
  • 需要注意的是,这并不会“实例化”对象(即不会调用构造函数等),它只是创建了一个同名的新类。

8
如果您有这个:
class MyClass:
    def __init__(self):
        print "MyClass"

通常情况下,您会这样做:
>>> x = MyClass()
MyClass

但你也可以这样做,我想这就是你所问的:
>>> a = "MyClass"
>>> y = eval(a)()
MyClass

但是,请非常小心地选择您使用 "eval()" 的字符串来源——如果它来自用户,则实际上会创建一个巨大的安全漏洞。

更新:如coleifer的答案中所示,使用type()要比这个解决方案优秀得多。


11
天啊,不要用eval函数,它是邪恶的。请勿使用。 - ddaa
@TimB,如果由我来决定,我认为这应该是最佳答案。 - Gang

1
我使用:

newObject = globals()[className]()

0

我更喜欢使用字典来存储类与字符串之间的映射关系。

>>> class AB:
...   def __init__(self, tt):
...     print(tt, "from class AB")
... 
>>> class BC:
...   def __init__(self, tt):
...     print(tt, "from class BC")
... 
>>> x = { "ab": AB, "bc": BC}
>>> x
{'ab': <class '__main__.AB'>, 'bc': <class '__main__.BC'>}
>>> 
>>> x['ab']('hello')
hello from class AB
<__main__.AB object at 0x10dd14b20>
>>> x['bc']('hello')
hello from class BC
<__main__.BC object at 0x10eb33dc0>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接