You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python类构造方法中self赋值引发AttributeError的原因解析

问题描述

我写了一个Test类,想通过同类实例创建新实例。在__init__方法里,当第二个参数是Test实例时执行self = a / b,否则给self绑定a、b属性。但调用Test(1, hf)时出现AttributeError: 'Test' object has no attribute 'a',调用1/Test(1, hf)时在__rtruediv__方法里也出现关于属性b的错误,想知道原因是不是self = ...的写法错了?

对应的代码:

class Test:
    def __init__(self, a, b):
        if type(b) == Test:
            self = a / b
        else:
            self.a = a
            self.b = b

    def __rtruediv__(self, other):
        return Test(other * self.b, self.a)

    def __str__(self):
        return f"{self.a = }, {self.b = }"


if __name__ == '__main__':
    hf = Test(1, 2)
    print(hf)  # self.a = 1, self.b = 2
    print(Test(1, hf))  # AttributeError: 'Test' object has no attribute 'a'
    print(1 / Test(1, hf))  # AttributeError: 'Test' object has no attribute 'b'
问题根源

没错,self = a / b的写法就是错误的,核心是对Python中__init__方法的作用理解有误:

  • __init__是实例初始化方法,它的作用是给已经创建好的实例绑定属性,而不是创建或替换实例本身。
  • self只是__init__方法的一个局部参数,指向当前创建的实例。你给self重新赋值,只会改变这个局部变量的指向,完全不会影响外部实际返回的那个Test实例。

当你调用Test(1, hf)时,Python会先创建一个空的Test实例,再调用__init__。此时进入if分支,self = a / b会生成一个新的Test实例,但这个赋值只在__init__内部有效,外部返回的还是最初那个没有a、b属性的空实例。后续print调用__str__时,访问self.a自然会报错。

第二个错误同理,Test(1, hf)返回的空实例没有b属性,所以1 / 该实例触发__rtruediv__时,访问self.b就会抛出异常。

修复方案

要实现“根据参数类型返回不同实例”的逻辑,不能在__init__里处理,推荐两种方式:

方式一:用类方法作为自定义构造器

把参数判断逻辑放到类方法里,__init__只负责基础的属性初始化:

class Test:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @classmethod
    def create(cls, a, b):
        if isinstance(b, cls):
            return a / b
        return cls(a, b)

    def __rtruediv__(self, other):
        return Test(other * self.b, self.a)

    def __str__(self):
        return f"{self.a = }, {self.b = }"


if __name__ == '__main__':
    hf = Test(1, 2)
    print(hf)  # self.a = 1, self.b = 2
    print(Test.create(1, hf))  # self.a = 2, self.b = 1
    print(1 / Test.create(1, hf))  # self.a = 1, self.b = 2

方式二:重写__new__方法

__new__才是Python中负责创建实例的方法,我们可以在这里处理参数判断逻辑:

class Test:
    def __new__(cls, a, b):
        if isinstance(b, cls):
            return a / b
        return super().__new__(cls)

    def __init__(self, a, b):
        # 只有当__new__返回当前类的实例时,才会执行__init__
        if not isinstance(b, Test):
            self.a = a
            self.b = b

    def __rtruediv__(self, other):
        return Test(other * self.b, self.a)

    def __str__(self):
        return f"{self.a = }, {self.b = }"


if __name__ == '__main__':
    hf = Test(1, 2)
    print(hf)  # self.a = 1, self.b = 2
    print(Test(1, hf))  # self.a = 2, self.b = 1
    print(1 / Test(1, hf))  # self.a = 1, self.b = 2

注意事项

  • 用isinstance(b, cls)代替type(b) == Test,这样能兼容子类实例,更符合Python的多态设计。
  • 方式二中,如果__new__返回的不是当前类的实例,__init__不会被执行,避免了无效的初始化操作。

内容的提问来源于stack exchange,提问作者Danny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:10:49