Skip to main content
 首页 » 编程设计

python之通过父初始化器以外的东西初始化子类的实例

2025年05月04日25zfyouxi

我有一个 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 创建您的实例。