我有一个 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
现在我想创建这个父类(super class)的一个子类,它的初始化总是来自一个文件——也就是说,我想做类似的事情
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 函数进行初始化?
请您参考如下方法:
Python 的 __init__ 方法实际上不是构造函数,它只是一个初始化器。这就是为什么您不能在 __init__ 方法中实现此行为的原因 - 一旦调用 __init__,您已经有一个多余的对象实例在周围 float 。
负责实际创建类的新实例的方法是__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()),这将返回一个完全单元化的对象实例(请记住,我们的 __init__ 是空的,因此该对象将没有任何属性)。不幸的是,我们对此无能为力。
一般来说,这样做可能不是一个好主意。我相信您现在已经知道,在尝试像这样更改现有类的接口(interface)时,必须注意许多陷阱。这很可能是不值得的,您应该坚持使用 SubClass.from_file 创建您的实例。
