从构造函数以正确的顺序调用加载函数

时间:2011-07-25 13:07:54

标签: python oop inheritance constructor

我有两个类,如下所示:

class Super(object):
    def __init__(self, arg):
        self.arg = arg

    def load(self):
        # Load data from disk if available
        try:
            self.data = load_routine()
        except IOError as e:
            if e[0] == errno.ENOENT:
                pass
            else:
                raise


class Sub(Super):
    def __init__(self, arg2, *args, **kwargs):
        super(Sub, self).__init__(*args, **kwargs)
        self.arg2 = arg2

    def load(self):
        # Load files specific to superclass
        super(Sub, self).load()
        # Load data from disk if available
        try:
            self.data2 = another_different_load_routine(self.arg2)
        except IOError as e:
            if e[0] == errno.ENOENT:
                pass
            else:
                raise

我希望代码足够清晰,除非我错过了一些内容,否则这应该适用于两个类,例如:obj = Super(); obj.load()

但是,我实际上希望在__init__()结束时自动调用load方法,但我不确定如何完成此操作。如果我在两个类的self.load()末尾添加__init__(),则子类的load()方法将被调用两次,如果尝试创建Sub的实例,则从超类和一次来自子类。

如果我只在超类的init方法结束时调用self.load(),它只会被调用一次,但是对象尚未初始化为包含加载所需的属性

在实例化Sub时,我想要的是为超级和子类初始化__init__中的所有属性,并为超级和子类调用load()。是否按顺序

Super -> __init__()Super -> load()Sub -> __init__()Sub -> load()

Super -> __init__()Sub -> __init__()Super -> load()Sub -> load()

并不重要。我怎么能做到这一点?

2 个答案:

答案 0 :(得分:1)

答案是您无法枚举的组合:

class Super(object):
    def __init__(self):
        print 'init super'
        if self.__class__ == Super:
            self.load()
    def load(self):
        print 'load super'

class Sub(Super):
    def __init__(self):
        # always do super first in init
        super(Sub, self).__init__()
        print 'init sub'
        self.load()
    def load(self):
        # load is essentially an extension of init
        # so you still need to call super first
        super(Sub, self).load()
        print 'load sub'

sub = Sub()

如果你真的想要实例化super(它不是一个抽象类),你需要在它的init中进行if测试。否则,您无法使用当前的类结构和初始化方案获得所需的有序语义。

Sub()会致电Sub.__init__,会致电Super.__init__(在做任何事情之前)。

之后,__init__上的Sub会做到这一点。

最后,Sub.__init__会调用Sub.load,它会调用Super.load(在执行任何操作之前),然后执行此操作。

执行此操作的“正常”方式是

sub = Sub()
sub.load()
sup = Super()
sup.load()

根本没有调用__init__的{​​{1}}方法。如果你真的想在load中调用级别,这可能就是我推荐的内容,因为它基本上是第二组load

编辑:阅读前两个版本中有关失败的注释(并查看编辑以查看旧版本)。这是另一个版本,使用元类:

__init__

这有一个不同的限制:所有子类都可以正常运行,除非他们无法使用class Loader(type): def __new__(cls, name, bases, attrs): if attrs.get('__init__'): attrs['_init'] = attrs['__init__'] del attrs['__init__'] if attrs.get('_init_'): attrs['__init__'] = lambda self: self._init_() attrs['_init'] = lambda self: None return super(Loader, cls).__new__(cls, name, bases, attrs) class Super(object): __metaclass__ = Loader def _init_(self): print 'init super' self._init() self.load() def load(self): print 'load super' class Sub(Super): def __init__(self): print 'init sub' def load(self): super(Sub, self).load() print 'load sub' sub = Sub() sup = Super() 调用Sub.__init__。我认为可以删除这个限制,但我没有看到在这一秒没有另一层间接的地方。

答案 1 :(得分:0)

如果您始终希望__init__()完全初始化对象,然后调用load(),那么我会将这两个函数提取到不同的方法中:

class Super(object):
    def __init__(self, *args, **kw):
        self._initialise(*args, **kw)
        self.load()

    def _initialise(self, arg):
        self.arg = arg

    def load(self):
        ... as before ...


class Sub(Super):
    def _initialise(self, arg2, *args, **kwargs):
        super(Sub, self)._initialise(*args, **kwargs)
        self.arg2 = arg2

    def load(self):
        super(Sub, self).load()
        ... as before ...

现在你有一个覆盖的__init__(),你不再混合初始化和加载的两个独立操作。