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
相关产品推荐
相关产品推荐

