动态加载继承模块

0 投票
1 回答
1596 浏览
提问于 2025-04-17 13:25

我知道这个话题已经有很多帖子讨论过了,但不管出于什么原因,我就是搞不懂,或者说实现起来有点困难。下面是我尝试做的代码示例。

基础类:

class Animal(object):

    def __init__(self, age):
        self._age = age

    def getAge(self):
        return self._age

    def speak(self):
        raise NotImplementedError()

    def speak_twice(self):
        self.speak()
        self.speak()

子类:

from Animal import Animal
class Dog(Animal):
    def speak(self):
        print "woff!"

测试代码:

mod = __import__("Dog")
spot = mod(5)

运行测试代码后,我遇到了这个错误:

Traceback (most recent call last):
  File "C:~test.py", line 2, in <module>
    spot = mod(5)
TypeError: 'module' object is not callable

所以我想问的是,怎样才能动态加载模块并正确初始化它们呢?

补充说明:

我在运行时才会知道子类是什么。

1 个回答

3

你需要先导入整个模块,然后再获取它里面的类成员。不能只导入类。假设你的子类在一个可以通过python路径访问的文件中,名字叫做'animal':

mod = __import__('animal')
spot = mod.Dog(5)

当你导入一个模块时,解释器首先会查看sys.modules中是否有这个名字的模块。如果找不到,它会在python路径中搜索,寻找与给定名称匹配的包或模块。如果找到了,它会解析里面的代码,创建一个模块对象,把它放到sys.modules中,然后把这个模块对象返回给调用的地方,绑定到你导入时使用的名字上。模块中的所有内容(类、变量、函数)在模块的作用域内(不是嵌套在其他代码里的)都可以作为这个模块实例的成员使用。

编辑:

针对你的评论,真正的问题是你试图动态查找模块的一个属性,而不是动态导入什么东西。最直接的做法是:

import sub_animal
getattr(sub_animal, 'Dog')

不过,如果你是想根据某些条件动态确定要初始化的类,你可能需要了解一下工厂模式,还有可能需要了解一下装饰器或者甚至元类,这样你就可以自动将子类动态添加到工厂中。

class AnimalFactory(type):

    animal_classes = {}

    def __new__(cls, name, bases, attrs):

        new_class = super(AnimalFactory, cls).__new__(cls, name, bases, attrs)
        AnimalFactory.animal_classes[name] = new_class
        return new_class

    @classmethod
    def build(cls, name, *args, **kwargs):

        try:
            klass = cls.animal_classes[name]
        except KeyError:
            raise ValueError('No known animal %s' % name)
        return klass(*args, **kwargs)

class Animal(object):

    __metaclass__ = AnimalFactory

    def __init__(self, age):

        self.age = age

    def speak(self):

        raise NotImplementedError()

# As long as the file it is implemented in is imported at some point,
# the following can be anywhere

class Dog(Animal):

    def speak(self):

        return 'woof'

# And then to use, again, anywhere

new_animal = AnimalFactory.build('Dog', 5)

撰写回答