Python:使用用户输入作为类名的类工厂

11 投票
2 回答
5644 浏览
提问于 2025-04-15 20:31

我想动态地给一个父类添加属性。同时,我还想动态创建从这个父类继承的子类,而这些子类的名字应该根据用户输入来决定。

现在有一个父类叫“Unit”,我可以在运行时给它添加属性。这部分已经可以正常工作了。

def add_attr (cls, name, value):
    setattr(cls, name, value)

class Unit(object):
    pass

class Archer(Unit):
    pass

myArcher = Archer()
add_attr(Unit, 'strength', 5)
print "Strenght ofmyarcher: " + str(myArcher.strength)
Unit.strength = 2
print "Strenght ofmyarcher: " + str(myArcher.strength)

这样就能得到想要的输出:
弓箭手的力量: 5
弓箭手的力量: 2

但是现在我不想提前定义子类“Archer”,而是想让用户来决定这个子类的名字。我尝试过类似的做法:

class Meta(type, subclassname):
    def __new__(cls, subclassname, bases, dct):
    return type.__new__(cls, subclassname, Unit, dct)

factory = Meta()    
factory.__new__("Soldier")  

但没有成功。我想我还没有真正理解new在这里的作用。 我想要的结果是

class Soldier(Unit):
    pass

由工厂创建。如果我用“Knight”作为参数调用工厂,我希望能创建一个名为“Knight”的类,它是Unit的子类。

有什么想法吗?非常感谢!
再见
-Sano

2 个回答

7

看看这个type()内置函数。

knight_class = type('Knight', (Unit,), {})
  • 第一个参数:新类的名字
  • 第二个参数:父类的元组
  • 第三个参数:类属性的字典

不过在你的情况下,如果子类没有实现不同的行为,给Unit类加一个name属性就足够了。

14

要根据名字创建一个类,可以使用 class 这个语句,并给它一个名字。看下面的例子:

def meta(name):
    class cls(Unit):
        pass

    cls.__name__ = name
    return cls

现在我想解释一下我的意思。当你用 class 语句创建一个类时,这个过程是动态的——这就相当于调用 type()

比如,下面这两段代码做的是一样的事情:

class X(object): pass
X = type("X", (object,), {})

类的名字——也就是传给 type 的第一个参数——会被赋值给 __name__,基本上就是这样了(__name__ 这个值通常只在默认的 __repr__() 实现中用到)。如果你想用动态名字创建一个类,实际上可以这样调用 type,或者你也可以在之后直接修改类的名字。不过,使用 class 语法是有原因的——它很方便,而且后续添加和修改东西也简单。如果你想添加方法,比如说,代码会是:

class X(object):
    def foo(self): print "foo"

def foo(self): print "foo"
X = type("X", (object,), {'foo':foo})

等等。所以我建议还是使用 class 语句——如果你一开始就知道可以这样做,你可能早就这么做了。直接使用 type 之类的会让事情变得很麻烦。

顺便说一下,不要手动调用 type.__new__(),只用 type() 就可以了。

撰写回答