源码探秘:Python 中对象是如何被调用的?

源码探秘:Python 中对象是如何被调用的?插图亿华云

楔子

我们知道对象被创建,主要有两种方式,一种是通过Python/C API,另一种是通过调用类型对象。对于内置类型的实例对象而言,这两种方式都是支持的,比如列表,我们即可以通过[]创建,也可以通过list(),前者是Python/C API,后者是调用类型对象。

但对于自定义类的实例对象而言,我们只能通过调用类型对象的方式来创建。而一个对象如果可以被调用,那么这个对象就是callable,否则就不是callable。

而决定一个对象是不是callable,就取决于其对应的类型对象中是否定义了某个方法。如果从 Python 的角度看的话,这个方法就是 __call__,从解释器角度看的话,这个方法就是 tp_call。

从 Python 的角度看对象的调用

调用 int、str、tuple 可以创建一个整数、字符串、元组,调用自定义的类也可以创建出相应的实例对象,说明类型对象是可调用的,也就是callable。那么这些类型对象(int、str、tuple、class等等)的类型对象(type)内部一定有 __call__ 方法。

# int可以调用

# 那么它的类型对象、也就是元类(type), 内部一定有__call__方法

print(hasattr(type, "__call__")) # True

# 而调用一个对象,等价于调用其类型对象的 __call__ 方法

# 所以 int(3.14)实际就等价于如下

print(type.__call__(int, 3.14)) # 3

注意:这里描述的可能有一些绕,我们说 int、str、float 这些都是类型对象(简单来说就是类),而 123、"你好"、3.14 是其对应的实例对象,这些都没问题。但type是不是类型对象,显然是的,虽然我们称呼它为元类,但它也是类型对象,如果 print(type) 显示的也是一个类。

那么相对 type 而言,int、str、float 是不是又成了实例对象呢?因为它们的类型是 type。

所以 class 具有二象性:

如果站在实例对象(如:123、"satori"、[]、3.14)的角度上,它是类型对象

THE END
Copyright © 2024 亿华云