如何动态设置类名?

58 投票
3 回答
41805 浏览
提问于 2025-04-16 13:58

我有一个函数,它可以根据传入的参数创建类:

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 个回答

16

可以试试用 type() 这个函数,它可以接收三个参数。下面的代码创建了一个新的类叫 "NewA",它的基础类型是 object,而且没有初始的属性。

>>> type('NewA', (object,), {})
<class '__main__.NewA'>
25

更新一下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命令搜索到;等等。

67

没错,设置 __name__ 是正确的做法;你不需要设置其他东西来调整类名。

举个例子:

def factory(BaseClass) :
    class NewClass(BaseClass): pass
    NewClass.__name__ = "factory_%s" % BaseClass.__name__
    return NewClass

type 在这里是 不对 的选择。它不允许你用 Python的正常类语法 来定义类,而是让你手动设置每个类的属性。它是用来手动创建类的,比如说你有一个基类的数组,想用它来创建一个类(这在Python的类语法中是做不到的)。所以在这里不要使用它。

撰写回答