我有一个Python类(它实际上不是我的,它来自一个包,所以我不想编辑这个)。最小的例子(不切实际):
import os
class SuperClass:
def __init__(self):
# Do something, for example
self.a = 0
pass
def from_file(filename):
# Do something with the file,
# use it to create superclass
# instance obj. For example...
obj = SuperClass()
obj.a = os.stat(filename).st_size
return obj
现在我想创建这个超类的子类,它的初始化总是来自一个文件 - 也就是说,我想做类似的事情
class SubClass(SuperClass):
def __init__(self, filename):
self = SuperClass.from_file(filename)
这不起作用(我没想到):我没有得到异常但是子类没有正确初始化。例如,如果SuperClass
有一些实例值a
,它总是被设置,那么
new_subclass_instance = SubClass('/filepath/file')
print(new_subclass_instance.a)
将提供AttributeError: type object 'SubClass' has no attribute 'a'
。
如何使用父类中的from_file
函数进行初始化?
答案 0 :(得分:5)
Python的__init__
方法实际上不是构造函数,它只是初始化程序。这就是为什么你不能在__init__
方法中实现这种行为的原因 - 一旦调用了__init__
,你就已经有了一个多余的对象实例。
负责实际创建类的新实例的方法是__new__
,因此我们可以通过覆盖它来实现我们的目标。基本的想法是写这样的东西:
class SubClass(SuperClass):
def __new__(cls, filename):
return cls.from_file(filename)
def __init__(self, *args, **kwargs):
pass
空__init__
是必要的,因为python会自动在__new__
返回的对象上调用它。如果我们省略空的__init__
方法,python将最终在对象上调用SuperClass.__init__
,这将重置其所有属性。
然而,有一个问题:由于我们已经重写__new__
方法以便它需要filename
参数,因此from_file
方法可能不再有效。你还没有展示它的代码,但它可能在这些方面做了一些事情:
@classmethod
def from_file(cls, filename):
obj = cls()
obj.filename = filename
return obj
这里的问题是对cls()
的调用将失败:这将调用我们的__new__
方法,该方法需要一个filename
参数,不带任何参数。我们最终会遇到像TypeError: __new__() missing 1 required positional argument: 'filename'
这样的例外情况。要解决此问题,我们可以将filename
参数设为可选:
class SubClass(SuperClass):
def __new__(cls, filename=None):
if filename is None:
return super().__new__(cls)
return cls.from_file(filename)
def __init__(self, *args, **kwargs):
pass
现在我们可以根据需要调用obj = SubClass('/filepath/file')
。 然而,它也可以在没有任何参数(如SubClass
)的情况下调用obj = SubClass()
,这将返回一个完全一致的对象实例(记住,我们的{{1}是空的,所以对象没有任何属性)。不幸的是,我们无能为力。
一般来说,这样做可能是一个坏主意。我相信你现在可以告诉我,在尝试改变现有类的界面时,有许多陷阱需要注意。它很可能不值得麻烦,您应该坚持使用__init__
创建实例。