我有两个类,如下所示:
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()
并不重要。我怎么能做到这一点?
答案 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__()
,你不再混合初始化和加载的两个独立操作。