CPython 解释器如何处理 OOP

How does the CPython Interpreter handle OOP

最近有朋友问"How does the CPython Interpreter actually handle OOP (Object Orientated Programming)?"。

这个问题最终让我感到困惑,因为我知道 C 不是一种面向对象的语言。

我试过了Googling it, searching Whosebug and even reading the CPython Wiki。但是我找不到任何有用的东西。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def getInfo(self):
        return "Name: " + self.name + "\nAge: " + str(self.age)

# How the heck does CPython handle this?
personOne = Person("Bob", 34)
personTwo = Person("Rob", 26)

print( personOne.getInfo() )
print( personTwo.getInfo() )

所以现在我真的很想知道!如果 CPython 解释器本身不是面向对象的,那么 CPython 解释器如何处理对象之类的东西?

这是一个小的思想实验:你的 CPU 根本不是 "object oriented"。相反,它只能执行 "add register 1 to register 2 and put the result in register 3" 和 "if register 5 is greater than zero, then execute this goto statement." 之类的指令,但不知何故 CPU 可以 运行 Python 和其他面向对象的语言。怎么样?

Python 的 OOP 实现的全部复杂性远远超出了 Stack Overflow 答案的范围,但可以提供一个概述。这将掩盖很多细节,例如元classes、多重继承、描述符和 C 级 API。不过,它应该让您了解为什么可以实现这样的事情,以及它是如何完成的一般印象。如果你想要完整的细节,你应该浏览 CPython source code.


像您的 Person class 实例的对象包含以下内容:

  • 一个class
  • 保存其属性的字典
  • 其他现在不相关的内容,例如 __weakref__

A class 也很简单。它有

  • 一个基地class
  • 保存其属性的字典
  • 其他现在不相关的东西,比如 class 名字。

当您使用 class 语句定义 class 时,Python 会捆绑指向您选择的基数 class 的指针(或 object 如果你没有选择一个)和一个包含你定义的方法的字典,这就是你的新 class 对象。有点像下面的元组

Person = (object,
          {'__init__': <that __init__ method you wrote>,
           'getInfo': <that getInfo method you wrote>},
          those irrelevant bits we're glossing over)

但不是元组。 (在 C 级别,此记录几乎(但不完全)作为结构实现。)


当您创建 class 的实例时,Python 将指向您的 class 的指针和实例属性的新字典捆绑在一起,这就是您的实例。它有点像下面的元组:

personOne = (Person, {}, those irrelevant bits we're glossing over)

但同样,不是元组。同样,它几乎(但不完全)作为 C 级别的结构实现。

然后它运行 __init__,传递 __init__ 新实例以及您提供给 class:

的任何其他参数
Person.__init__(personOne, "Bob", 34)

属性赋值被转换为对象字典中的设置条目,因此 __init__:

中的赋值
def __init__(self, name, age):
    self.name = name
    self.age = age

使字典以下列状态结束:

{'name': 'Bob', 'age': 34}

当你调用 personOne.getInfo() 时,Python 查看 personOne 的字典,然后是 class 的字典,然后是它的 superclass' s dict 等,直到找到 'getInfo' 键的条目。关联值将是 getInfo 方法。如果在 class 字典中找到该方法,Python 将插入 personOne 作为第一个参数。 (它如何知道插入该参数的详细信息在 descriptor protocol 中。)