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

Python中类与实例的__init__方法创建及调用机制解惑

Python中__init__方法的创建与调用核心解析
# define a class, with an __init__ method
class A(object):
    def __init__(self, value):
        self.value = value


# instantiate regularly
a1 = A(1)
# or using the class's __call__
a2 = A.__call__(2)

assert a1.value == 1
assert a2.value == 2

# notice the instance of A has an __init__ method
assert hasattr(a1, "__init__")
# ... but no __call__ method [which makes sense]
assert not hasattr(a1, "__call__")
# A also has an __init__ method [which I thought would initialize A itself, and not its instances!]
assert hasattr(A, "__init__")

# I expected the instance's __init__ to be different than the class's __init__, which is the case.
A_init = A.__init__
init1 = a1.__init__
init2 = a2.__init__
assert init1 != init2
assert init1 != A_init
assert init2 != A_init


# However, the classes __init__ DOES NOT INITIALIZE THE CLASS! It initialized THE INSTANCE PROVIDED.
# The classes __init__ is not a bound method with its first argument `cls` as I expected. - What is it then?
# Does that mean the classes __init__ is actually a @classmethod?
init1(3)
init2(4)
assert a1.value == 3
assert a2.value == 4
A_init(a1, 5)  # This is a surprise 
assert a1.value == 5

# Let's try to invalidate the instance's __init__
# delattr(a1, "__init__") --> This doesn't remove the attribute - why?
a1.__init__ = lambda self, *args, **kwargs: None
a1.__init__(6)
# We see the instance's __init__ has changed successfully
assert a1.value == 5
# And the class's __init__ can still be used.
A.__init__(a1, 7)
assert a1.value == 7

# Let's now invalidate the class's init
A.__init__ = lambda self, *args, **kwargs: None
A.__init__(a2, 8)
assert a2.value == 4

# What's going on here? We never assigned into a2.__init__!
assert a2.__init__ != init2

# This makes sense, as init2 is bound to a2
init2(9)
assert a2.value == 9

# This doesn't make sense to me. It seems changing A's init somehow also changed the call to a2's
# but method resolution order can't explain that, because a2's __init__ should be called!
assert hasattr(A, "__init__")
assert hasattr(a2, "__init__")
a2.__init__(10)
assert a2.value == 9

1. 类与实例的__init__方法分别是何时、如何创建的?

  • 类的__init__:定义类时,__init__作为普通函数被创建并挂载到类对象上,它本质是未绑定函数,self只是约定的参数名,没有自动绑定逻辑。
  • 实例的__init__:当访问实例的__init__时,Python会沿着继承链从类中找到该函数,自动包装成绑定方法——把当前实例预先绑定为第一个参数self。每次访问实例的__init__都会生成新的绑定方法对象,这就是init1 != init2的原因。

2. 不同场景下会调用哪个__init__方法?

  • 创建实例A(value)时,Python调用类的__call__方法(由元类type提供),先通过__new__创建实例,再自动调用类的__init__,并将新实例作为第一个参数传入。
  • 直接调用a1.__init__(x)时,调用的是绑定到a1的方法,等价于A.__init__(a1, x)(除非给实例单独赋值了__init__)。
  • 若给实例手动赋值a1.__init__ = lambda ...,访问a1.__init__会优先取实例自身的属性,不再从类中查找。

3. 为何无法使用delattr(a1, "init")删除实例的__init__属性?

实例本身并没有存储__init__属性,hasattr(a1, "__init__")返回True是因为Python的属性查找机制会遍历类、父类的继承链。实例的__init__是动态生成的绑定方法,不属于实例自身的属性,所以delattr找不到可删除的目标。只有给实例手动赋值过a1.__init__后,delattr(a1, "__init__")才能生效。

4. 上述现象是否适用于所有方法或魔法方法?__init__是特殊情况吗?还有其他特殊情况吗?

大部分普通方法和魔法方法都遵循这个逻辑,但存在例外:

  • 静态方法(@staticmethod):不会被包装成绑定方法,从类或实例访问都是同一个函数对象。
  • 类方法(@classmethod):会绑定到类对象,从类或实例访问都是绑定到类的方法,调用时自动传入类作为第一个参数cls。
  • 特殊魔法方法:比如__new__本质是类方法(无需@classmethod装饰),参数为cls负责创建实例;少数魔法方法如__str__在特定场景下会直接从类中查找,不过多数场景仍遵循属性查找顺序。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 01:05:25