如何在动态语言中实现类?

时间:2012-09-24 01:23:41

标签: language-agnostic programming-languages

如何用动态语言实现类?

我知道Javascript正在使用原型模式('某处'是​​未绑定的JS函数的容器,在通过对象调用它们时会绑定),但我不知道它在其他语言中是如何工作的。< / p>

我对此感到好奇,因为我无法想到一种有效的方法来获取本地绑定方法而不会通过复制每个实例的成员来浪费内存和/或cpu。

(通过绑定方法,我的意思是以下代码应该起作用:)

class Foo { function bar() : return 42; };

var test = new Foo();
var method = test.bar;
method() == 42;

1 个答案:

答案 0 :(得分:1)

这在很大程度上取决于语言的实现。我会告诉你我对CPython和PyPy的了解。

一般的想法,也是CPython在大多数情况下所做的事情,如下所示:

  1. 每个对象都有一个类,特别是对该类对象的引用。
  2. 除了显然存储在单个对象中的实例成员之外,该类还具有成员。这包括方法,因此方法没有每个对象的成本。
  3. 类具有由继承关系确定的方法解析顺序(MRO),其中每个基类恰好出现一次。如果我们没有多重继承,那么这只是对基类的引用,但这样MRO很难在运行中找出(你必须每次都从最派生的类开始)。 / LI>
  4. (类也是对象,并且本身也有类,但我们现在要掩盖它们。)
  5. 如果对象上的属性查找失败,则按照MRO指定的顺序在MRO中的类上查找相同的属性。 (这是默认行为,可以通过定义__getattr____getattribute__等魔术方法来更改。)
  6. 到目前为止这么简单,并不是对绑定方法的真正解释。我只是想确保我们谈论同样的事情。缺少的部分是描述符descriptor protocol在语言参考的“深层魔术”部分中定义,但简短而简单的故事是,对类的查找可以通过其导致的对象被劫持 __get__方法。更重要的是,这个__get__方法被告知是在实例上还是在“所有者”(该类)上查找

    在Python 2中,我们有一个丑陋且不必要的UnboundMethod描述符(除了__get__方法之外)只是简单地包含该函数,以便Class.method(self) self时抛出错误是不可接受的类型。在Python 3中,__get__只是所有函数对象的一部分,未绑定的方法消失了。在这两种情况下,__get__方法在您查找类时会返回自身(因此您可以使用Class.method,这在一些情况下很有用)和“绑定方法”对象它在一个物体上。这个绑定的方法对象只是存储原始函数和实例,并将后者作为第一个参数传递给它的__call__(覆盖函数调用语法的特殊方法)。

    所以,对于CPython:虽然绑定方法有成本,但它比你想象的要小。在空间方面只需要两个引用,并且CPU成本仅限于小内存分配,并且在调用时需要额外的间接。请注意,此成本适用于所有方法调用,而不仅仅是那些实际使用绑定方法功能的方法调用。 a.f() 必须调用描述符并使用其返回值,因为在动态语言中我们不知道是否进行了猴子修补以执行不同的操作。

    在PyPy中,事情更有趣。因为它是一个不会在正确性上妥协的实现,所以上述模型对于语义推理仍然是正确的。但是,它实际上更快。除了JIT编译器在大多数情况下内联然后消除上述整个混乱之外,它们还解决了字节码级别的问题。有两个new bytecode instructions,它们保留了语义,但在a.f()的情况下省略了绑定方法对象的分配。还有一个method cache可以简化查找过程,但需要一些额外的簿记(尽管已经为JIT完成了一些簿记)。