有没有办法动态创建子类?

12 投票
3 回答
11623 浏览
提问于 2025-04-17 12:59

我正在制作一个游戏,里面有一个比较复杂的方法来创建游戏角色。

当加载一个关卡时,加载的代码会读取一堆YAML文件,这些文件里包含了各种不同单位的属性。通过这些YAML文件,它会创建一个叫做EntityResource的对象。这个EntityResource对象是生成新单位时的信息来源,算是权威的资料。这样做有两个目的:

  1. 通过对YAML文件输出进行哈希检查,来防止作弊。
  2. 通过让所有单位的信息来自一个权威的来源,来帮助调试。

这些EntityResource对象接着会被传入一个EntityFactory对象,以生成特定类型的单位。

我的问题是:有没有办法根据读取的YAML文件内容动态创建EntityResource的子类?

另外,我希望这些从YAML文件派生的子类都能被分配一个单例元类。这样做有什么注意事项吗?

3 个回答

1

当我听到“动态创建子类”时,我理解为“创建一些在运行时表现不同的对象”,这其实是关于配置的问题。

你有没有什么需求,是通过简单读取一些数据,然后创建一个对象,根据这些数据决定它的行为,无法满足的呢?

这里有个比喻:我很能干——无论你给我什么宜家家具,我都能组装起来。但我每次并不是变成一个不同的人,我只是同一个能干的人,阅读不同的图纸,寻找不同种类的螺丝和木头。这就是我认为在这里使用子类并不是自然解决方案的原因。

7

你可以随时创建子类,但这并不意味着你应该这样做。无论如何,我会提供一种机制来实现。

每个类都有一个bases属性,它告诉你这个类的继承关系:

class Animal(object):
    pass

class Dog(Animal):
    pass

print Animal.__bases__
print Dog.__bases__
# prints:
#(<type 'object'>,)
#(<class '__main__.Animal'>,)

所以,__bases__ 是一个包含“继承基础”的元组。你可以替换这个元组(因为它是元组,所以你不能“添加”或“删除”其中的元素,元组是不可变的)。举个例子,假设你有一个“混入类”,它为某些动物子类添加功能,但对其他子类则没有:

class Animal(object):
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

class TalkMixin(object):
    def talk(self):
        print("I talk like a {0}".format(self.__class__.__name__))

if __name__ == "__main__":

    dog = Dog()
    cat = Cat()

    try:
        dog.talk() 
        cat.talk()
    except AttributeError:
        print("Great - the program raised AttributeError, as expected")

    # now, add the MixIn to the class "Dog", but not to the class
    # "Cat" - do this on the fly:
    old_dog_bases = Dog.__bases__
    Dog.__bases__ = (Animal, TalkMixin)

    # this should be successful!
    dog.talk()

    try:
        cat.talk()
    except AttributeError:
        print("As expected, cat.talk() raised AttributeError")

    # now do the same (add the mixin dynamically - via inheritance) to
    # the Cat
    old_cat_bases = Cat.__bases__
    Cat.__bases__ = (Animal, TalkMixin)

    # this should be successful!
    cat.talk()

    # Now, remove the mixin (drop from the inheritance) for both cats
    # and dogs:
    Dog.__bases__ = old_dog_bases
    Cat.__bases__ = old_cat_bases

    try:
        dog.talk()
        cat.talk()
    except AttributeError:
        print("as expected, they can no longer talk")

这会产生以下输出:

Great - the program raised AttributeError, as expected
I talk like a Dog
As expected, cat.talk() raised AttributeError
I talk like a Cat
as expected, they can no longer talk

很多人认为混入类是有害的,可能他们说得对!你可以想象,如果你搞乱了bases属性,你的程序基本上就毁了。所以,虽然你可以动态地改变一个对象的继承关系,但这并不意味着你应该这样做(可能更好的选择是抽象类或概念实现?)

39

我不确定这是不是你想要的,但你可以用 type 来动态创建子类:

SubClass = type('SubClass', (EntityResource,), {})

补充一下:要理解 type 是怎么工作的,你只需要把你写类的方式转换成 type 的调用方式。比如,如果你想写类似这样的代码:

class SubClass(EntityResource):
    A=1
    B=2

那么,这就可以转换成:

 SubClass = type('SubClass', (EntityResource,), {'A': 1, 'B': 2})

其中:

  • 第一个参数就是类的名字
  • 第二个参数是父类的列表
  • 第三个参数是一个字典,用来初始化类的对象。这不仅包括类的属性,还包括方法。

撰写回答