Python中如何理解类实例化的输出?

class Meta_1(type):
def call(cls, *a, **kw):
print(“entering Meta_1.call()”) #语句 1
rv = super(Meta_1, cls).call(*a, **kw) #语句 2
print(“exiting Meta_1.call()”) #语句 3
return rv

class Class_1(object,metaclass = Meta_1):

def new(cls, *a, **kw):

print(“entering Class_1.new()”) #语句 4
rv = super(Class_1, cls).new(cls, *a, **kw)
print(“exiting Class_1.new()”)
return rv

def init(self, *a, **kw):
print(“executing Class_1.init()”)
super(Class_1,self).init(*a, **kw)

print("—test—")
c = Class_1()


输出如下:
—test—
entering Meta_1.call()
entering Class_1.new()
exiting Class_1.new()
executing Class_1.init()
exiting Meta_1.call()


我的问题是:
1、模块被加载时,类定义的__new__和__init__就应该被执行了吧?为何从上面程序的输出来看并没有执行,因为第一个输出就是"—test—",说明是等到实际执行程序主体时才发生的输出。
2、类定义的__new__和__init__应该是在__call__方法之前执行的吧,但为何实际上先执行的却是 Meta_1 的__call__方法,而不是 Class_1 的__new__和__init__?
2、从输出来看,语句 1 执行完毕后,不是执行紧接着的语句 2,而是去执行语句 4 了,这又是为什么呢?
Python中如何理解类实例化的输出?


18 回复
  1. 类在被实例化的时候才会调用类方法__new__ , 之后才会调用实例的 init 方法. 模块被加载你指的是什么时候?
    import module 的时候 ?
    2. 前面一句没有错. 但是 Meta_1 和 Class_1 又不是同属一个类. 一般元类的__call__ 里面才会调用 Class_1 的 __new__来生成一个新的类.
    3. 没有证据看到不是执行语句 2…不知道怎么说

在Python中,类实例化的输出默认是<__main__.ClassName object at 0x内存地址>这种形式。这其实是对象调用__repr__()方法返回的字符串表示。

举个例子:

class MyClass:
    def __init__(self, value):
        self.value = value

obj = MyClass(42)
print(obj)  # 输出: <__main__.MyClass object at 0x...>

这个输出包含:

  • __main__:表示当前模块
  • MyClass:类名
  • object:表示这是一个对象
  • 0x...:对象在内存中的地址(十六进制)

如果你想自定义输出,可以重写__repr__()方法:

class MyClass:
    def __init__(self, value):
        self.value = value
    
    def __repr__(self):
        return f"MyClass(value={self.value})"

obj = MyClass(42)
print(obj)  # 输出: MyClass(value=42)

__repr__()应该返回一个明确的、可以用于重新创建对象的字符串。调试时用__repr__(),给用户看用__str__()

简单说就是:默认输出是内存地址,想改就重写__repr__

非常感谢指点!
1、我说的模块被加载的时候就是指 import 的时候,也就是在执行主程序体第一条语句( print("—test—") )之前的时间。
2、这个例子中,元类的__call__ 里面并没有调用 Class_1 的 __new__吧?
3、语句 2 是执行的是 Meta_1 的元类,也就是 type 的__call__方法没错吧?难道这条语句会导致语句 4 的执行,如果不是导致执行语句 4 的话,就不会有这个输出了:“ entering Class_1.new() ”

首先,类只有在实例化(即 类名(args))的时候才会先调用__new__再调用__init__
第二,metaclass 就是负责实例化这个过程的,即 类名(args)这个调用等同于 Meta 类名.call(类名,args)
而你自定义的类中的__new__和__init__本质上是 type.__call__调用的,所以造成了你看到的输出顺序

我之所以说在 import module 时,类定义中的__new__和__init__会被先执行是有例子的,参见下面的代码,
在主程序第一条语句(语句 1)之前,语句 2 和语句 3 就先被执行了,也就说__new__和__init__方法在执行主程序之前就执行了。我的理解对么?

<p>class&nbsp;MyType(type):&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;init(self,&nbsp;what,&nbsp;bases=None,&nbsp;dict=None):&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super().init(what,&nbsp;bases,&nbsp;dict)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“call&nbsp;MyType.init()”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#语句 3
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;new(cls,&nbsp;name,&nbsp;bases,&nbsp;attrs):&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“call&nbsp;MyType.new()”)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#语句 2
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;type.new(cls,&nbsp;name,&nbsp;bases,&nbsp;attrs)&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;call(self,&nbsp;*args,&nbsp;**kwargs):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“MyType.call&nbsp;”)
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;=&nbsp;self.new(self,&nbsp;*args,&nbsp;**kwargs)&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.init(obj)&nbsp;

</p><p>class&nbsp;Foo(object,&nbsp;metaclass=MyType):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;init(self,&nbsp;name=None):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.name&nbsp;=&nbsp;name&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“Foo.init&nbsp;self=”,&nbsp;self)&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;new(cls,&nbsp;*args,&nbsp;**kwargs):&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“Foo.new&nbsp;cls=”,&nbsp;cls)&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(object.new(cls,&nbsp;*args,&nbsp;**kwargs))&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;def&nbsp;call(self,&nbsp;cls):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(“Foo.call&nbsp;cls=”,&nbsp;cls)&nbsp;&nbsp;
</p><p>&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</p><p>if&nbsp;name&nbsp;==&nbsp;‘main’:&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;print("---------test1---------")&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#语句 1&nbsp;
</p><p>&nbsp;&nbsp;&nbsp;&nbsp;obj2=Foo()</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

</p><p>输出如下:
</p><p>call&nbsp;MyType.new()
</p><p>call&nbsp;MyType.init()
</p><p>---------test1---------
</p><p>MyType.call&nbsp;
</p><p>Foo.new&nbsp;cls=&nbsp;<class&nbsp;‘main.Foo’>
</p><p>Foo.init&nbsp;self=&nbsp;<main.Foo&nbsp;object&nbsp;at&nbsp;0x01C2B2B0>
</p>

抱歉上面的格式太乱了,我不知道回复时不会做格式转换,下面是重新整理后的内容。
我之所以说在 import module 时,类定义中的__new__和__init__会被先执行是有例子的,参见下面的代码,
在主程序第一条语句(语句 1)之前,语句 2 和语句 3 就先被执行了,也就说__new__和__init__方法在执行主程序之前就执行了。我的理解对么?

class MyType(type):
def init(self, what, bases=None, dict=None):
super().init(what, bases, dict)
print(“call MyType.init()”) #语句 3
def new(cls, name, bases, attrs):
print(“call MyType.new()”) #语句 2
return type.new(cls, name, bases, attrs)
def call(self, *args, **kwargs):
print(“MyType.call “)
obj = self.new(self, *args, **kwargs)
self.init(obj)

class Foo(object, metaclass=MyType):
def init(self, name=None):
self.name = name
print(“Foo.init self=”, self)
def new(cls, *args, **kwargs):
print(“Foo.new cls=”, cls)
return(object.new(cls, *args, **kwargs))
def call(self, cls):
print(“Foo.call cls=”, cls)


if name == ‘main’:
print(”---------test1---------”) #语句 1
obj2=Foo()

输出如下:
call MyType.new()
call MyType.init()
---------test1---------
MyType.call
Foo.new cls= <class ‘main.Foo’>
Foo.init self= <main.Foo object at 0x01C2B2B0>

关于您说的第二点:“ metaclass 就是负责实例化这个过程的,即 类名(args)这个调用等同于 Meta 类名.call(类名,args) 而你自定义的类中的__new__和__init__本质上是 type.__call__调用的,所以造成了你看到的输出顺序”。我还是不明白。
我知道“ c = Class_1() ”会调用 Meta 类的 call 方法,我不理解的是执行这个 call 方法时,遇到这条语句“ rv = super(Meta_1, cls).call(*a, **kw) ”时到底发生了什么事,这时不就是执行 type.__call__方法么?怎么会输出“ entering Class_1.new() ” ? 这个输出难道不是只有在执行 Class_1.__new__方法时才会输出的么?

因为 type.call.调用了 Class_1.__new__呀…

你上面那个情况先出来 call 两个 MyType 的情况是因为你指定了 MyType 作为 Class_1 的 metaclass,所以 python 先实例化了一个 MyType 给 Class_1 备用

你要明白 python 是解析式语言,所以语句是顺序执行的,当你定义 Class_1 并指定 MyType 作为它的 metaclass 的时候就隐含的初始化了一个 MyType 的实例
而你的 main 是放在最后的,所以当然是后输出___test1___

你要注意看就会发现__call__方法是一个绑定方法,即他需要一个 self 参数,也就是说他必须在一个对象实例上调用,而 python 不可能每次在实例化 Class_1 的时候都实例化一个新的 MyType,这样又耗费性能和内存,又完全没有意义,所以他是在你类定义的时候就初始化了一个 MyType 的实例,然后每次实例化 Class_1 的时候都调用这个 MyType 的实例的__call__方法
最后,实际上你初始化一个 Class_1 实例的调用栈是这样的

Class_1() -> 定义类的时候保存的 MyType 的实例.__call() -> type.call -> Class_1.new -> object.new -> Class_1.init

然后反向出栈

非常感谢您的耐心解答,我发现我现在就卡在这一句话上了:
“因为 type.call.调用了 Class_1.__new__呀…” 不明白这是一个什么知识点,我也查过一些博文和书,但是都没有深入提到这一点,能否再详细说明一下,或者有关于这点的透彻说明的文档推荐也行,万分感谢了!

这个地方你不需要明白,只要记住就行了,所有__xx__的方法都是用来给解析器或者系统库隐式调用的,至于具体为什么,你需要去看 cpython 的源代码,我记得这一部分是用 c 写的。就好比 c++初始化一个类就会调用构造方法一样,这个是编译器行为,过度探究原因并没有什么意义

我给你找了一下 type.__call__的具体实现,你可以自己看看
https://github.com/python/cpython/blob/master/Objects/typeobject.c#L921

太感谢你了!
既然 MyType 作为 Class_1 的 metaclass,导致 Class_1 类定义的时候会自动实例化一个 MyType 实例。
那么如果程序还有一个类 Class_2,且 Class_1 是 Class_2 的父类,那么在执行 Class_2 类定义的时候,是不是还会另外多执行一遍 MyType 的实例化动作(执行 new 和 init)?也就是说 Class_1 有多少个之类,MyType 就会被自动实例化多少次?
我从代码的输出来看,推断应该是这个处理方式,但是没想明白,为何在定义子类的时候,竟然会去将父类(Class_1)的元类进行实例化? 以及为何每个子类都要实例化一次元类,而不能所有的子类公用定义父类时实例化出来的元类?

cpython 的源码我准备去看看,但是未必都能看懂,关于这一个知识点我准备硬记下来,请您看看我这么归纳对么?
存在一个类 Meta,Meta 是类 Clas 的元类,那么对于 Meta 类定义中的 type.__call__方法调用,实际上是去调用了类 Clas 的__new__和__init__方法

至于为什么子类不公用父类的元类实例,这个问题其实很好解释,你有注意到 Meta 类的__init__方法了么,他传递进来了一个 what 参数不知道你注意到没,这个参数实际上传进来的就是你定义的 Class 这个类本身(注意不是 Class 的实例,是 Class.class),而如果你的子类公用了父类的元类实例,这个参数就会导致问题,他将无法正常初始化一个子类对象而仅仅是初始化出来一个父类对象而已

至于你说的第二点,仔细看看我给你找的源码就应该知道,type.__call__做的事情其实就是调用了类的__new__来生成了一个空对象,然后把这个对象传入__init__来初始化,所以你的归纳基本上正确

顿首拜!!!

回到顶部