通过父初始化程序以外的方式初始化子类的实例

Mee*_*ies 1 python class subclass superclass

我有一个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
Run Code Online (Sandbox Code Playgroud)

现在我想创建这个超类的子类,它的初始化总是来自一个文件 - 也就是说,我想做类似的事情

class SubClass(SuperClass):
    def __init__(self, filename):
        self = SuperClass.from_file(filename)
Run Code Online (Sandbox Code Playgroud)

这不起作用(我没想到):我没有得到异常但是子类没有正确初始化.例如,如果SuperClass有一些实例值a,总是设置,那么

new_subclass_instance = SubClass('/filepath/file')
print(new_subclass_instance.a)
Run Code Online (Sandbox Code Playgroud)

会给一个AttributeError: type object 'SubClass' has no attribute 'a'.

如何使用from_file父类中的函数初始化?

Ara*_*Fey 5

Python的__init__方法实际上不是构造函数,它只是一个初始化器.这就是为什么你不能在__init__方法中实现这种行为- 一旦__init__被调用,你已经有一个多余的对象实例浮动.

负责实际创建类的新实例的方法是__new__,所以我们可以通过覆盖它来实现我们的目标.基本的想法是写这样的东西:

class SubClass(SuperClass):
    def __new__(cls, filename):
        return cls.from_file(filename)

    def __init__(self, *args, **kwargs):
        pass
Run Code Online (Sandbox Code Playgroud)

__init__是必要的,因为python会自动在返回的对象上调用它__new__.如果省略空__init__方法,python将最终调用SuperClass.__init__该对象,这将重置其所有属性.

但是有一个问题:由于我们已经重写了__new__方法以便它需要filename参数,因此该from_file方法可能不再有效.你没有展示它的代码,但它可能在这些方面做了些什么:

@classmethod
def from_file(cls, filename):
    obj = cls()
    obj.filename = filename
    return obj
Run Code Online (Sandbox Code Playgroud)

这里的问题是调用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
Run Code Online (Sandbox Code Playgroud)

现在我们可以obj = SubClass('/filepath/file')根据需要打电话.但是,也可以在SubClass没有任何参数(如obj = SubClass())的情况下调用,这将返回一个完全单元化的对象实例(记住,我们__init__是空的,因此对象将没有任何属性).不幸的是,我们无能为力.

一般来说,这样做可能是一个坏主意.我相信你现在可以知道,在尝试更改现有类的界面时,有许多陷阱需要注意.它很可能不值得麻烦,你应该坚持创建你的实例SubClass.from_file.