如何动态设置类名?
我有一个函数,它可以根据传入的参数创建类:
def factory(BaseClass) :
class NewClass(BaseClass) : pass
return NewClass
现在,当我用这个函数创建新类的时候,所有的类名字都是一样的,而且这些实例看起来好像都是同一种类型:
NewA = factory(ClassA)
NewB = factory(ClassB)
print type(NewA()) # <class __main__.NewClass>
print type(NewB()) # <class __main__.NewClass>
解决这个问题的正确方法是手动设置 __name__
属性吗?
NewA.__name__ = 'NewA'
print type(NewA()) # <class __main__.NewA>
在这个过程中,还有其他需要设置的东西吗?
3 个回答
可以试试用 type()
这个函数,它可以接收三个参数。下面的代码创建了一个新的类叫 "NewA",它的基础类型是 object
,而且没有初始的属性。
>>> type('NewA', (object,), {})
<class '__main__.NewA'>
更新一下Glenn Maynard的回答:现在有两个属性,分别是__name__
和__qualname__
。第一个属性是你想的那样,第二个属性是用于嵌套类的“路径”,用点号分隔。
对于“简单”的类来说,这两个属性是一样的。只需要把__name__
和__qualname__
都设置成你想要的新名字就可以了。最好同时设置这两个属性,因为你无法确定第三方代码会查找哪个。
现在对于嵌套类,这两个属性的区别就显现出来了:
class Outer:
class Inner:
pass
print(Outer.__name__, Outer.__qualname__)
print(Outer.Inner.__name__, Outer.Inner.__qualname__)
打印结果是:
Outer Outer
Inner Outer.Inner
如果你想要改变Outer
的名字,你需要修改三个地方,分别是Outer.__name__
、Outer.__qualname__
和Inner.__qualname__
。对于后两个属性,你需要正确地在点号处分割和连接。
最后提醒一下:即使你都做对了,像sphinx、pylint等工具可能还是不能100%正常工作。例如,假名字在模块命名空间中找不到;类定义的源代码也无法用grep
命令搜索到;等等。
没错,设置 __name__
是正确的做法;你不需要设置其他东西来调整类名。
举个例子:
def factory(BaseClass) :
class NewClass(BaseClass): pass
NewClass.__name__ = "factory_%s" % BaseClass.__name__
return NewClass
type
在这里是 不对 的选择。它不允许你用 Python的正常类语法 来定义类,而是让你手动设置每个类的属性。它是用来手动创建类的,比如说你有一个基类的数组,想用它来创建一个类(这在Python的类语法中是做不到的)。所以在这里不要使用它。