在Python中动态创建类及其构造函数

1 投票
1 回答
1357 浏览
提问于 2025-04-17 23:35

我需要创建很多子类,都是从一个给定的类派生出来的。我不想手动去做这件事,因为类的数量可能会非常多。我有一个几乎可以工作的解决方案……差一点——看看下面的例子,帮我看看我哪里做错了。

ZOO_CLASSES这个字典里存放着每种动物(用字符串表示)对应的类。

最终的解决方案必须使用动物这个基类的继承,并且每种动物的对象必须通过

one_lion = Lion()

或者

one_lion = ZOO_CLASSES['Lion']()

来创建。

我发现的问题是(如果你运行测试就会看到——我得到的结果是“True, True, This is Parrot, This is Parrot”,而不是“True, False, This is Lion, This is Bear”)。我觉得问题出在调用构造函数并评估它的参数(self和i)时,它取的是最后赋值的结果(i="Parrot")。不过,我创建的对象类型是正确的,我没有看到其他意外的行为。

顺便说一下,我是Python新手。:)

class Animal:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return "This is " + self.name

    def __eq__(self, other):
        return self.name == other.name

ZOO = ['Lion', 'Bear', 'Parrot'] # list with many animals

ZOO_CLASSES = {}
for i in ZOO:
    ZOO_CLASSES[i] = type(i, (Animal,), dict(__init__=lambda self: Animal.__init__(self, i)))

# or even tried:
for i in ZOO:
    def constructor(self):
        Animal.__init__(self, i)
    ZOO_CLASSES[i] = type(i, (Animal,), dict(__init__=constructor))

# each time it creates a different function (as should be) but the value of i stays to the last one in the list:
# tests:
print(ZOO_CLASSES["Lion"]() == ZOO_CLASSES["Lion"]()) # True
print(ZOO_CLASSES["Lion"]() == ZOO_CLASSES["Bear"]()) # False
print(str(ZOO_CLASSES["Lion"]())) # This is Lion
print(str(ZOO_CLASSES["Bear"]())) # This is Bear
# i.e. all times we call these classes (their constructors) - i gets evaluated as "Parrot" (last element of ZOO list)

###################
# I don't want to do this (because imagine if the list ZOO is really long,
# manually typing it would be stupid):
class Lion(Animal):
    def __init__(self):
        Animal.__init__(self, "Lion")

class Bear(Animal):
    def __init__(self):
        Animal.__init__(self, "Bear")

class Parrot(Animal):
    def __init__(self):
        Animal.__init__(self, "Parrot")

1 个回答

1

问题在于,lambda里面的i变量只有在你创建类的实例时才会被计算。而这个时候,循环已经结束,i的值会被设置为列表中的最后一个项目,也就是Parrot

你应该把i传递给lambda

ZOO_CLASSES[i] = type(i, 
                      (Animal,), 
                      dict(__init__=lambda self, i=i: Animal.__init__(self, i)))
                                                 ^

示例:

>>> class Animal:
...     def __init__(self, name):
...         self.name = name
... 
>>> ZOO = ['Lion', 'Bear', 'Parrot']
>>> ZOO_CLASSES = {}

>>> for i in ZOO:
...     ZOO_CLASSES[i] = type(i, (Animal,), dict(__init__=lambda self: Animal.__init__(self, i)))
... 
>>> i
'Parrot'
>>> ZOO_CLASSES["Lion"]().name
'Parrot'

>>> for i in ZOO:
...     ZOO_CLASSES[i] = type(i, (Animal,), dict(__init__=lambda self, i=i: Animal.__init__(self, i)))
... 
>>> ZOO_CLASSES["Lion"]().name
'Lion'

另外,感谢@BrenBarn的评论,这里有更好的解释:Python lambda 闭包作用域

希望这能帮到你。

撰写回答