如何用动态语言实现类?
我知道Javascript正在使用原型模式('某处'是未绑定的JS函数的容器,在通过对象调用它们时会绑定),但我不知道它在其他语言中是如何工作的。< / p>
我对此感到好奇,因为我无法想到一种有效的方法来获取本地绑定方法而不会通过复制每个实例的成员来浪费内存和/或cpu。
(通过绑定方法,我的意思是以下代码应该起作用:)
class Foo { function bar() : return 42; };
var test = new Foo();
var method = test.bar;
method() == 42;
答案 0 :(得分:1)
这在很大程度上取决于语言和的实现。我会告诉你我对CPython和PyPy的了解。
一般的想法,也是CPython在大多数情况下所做的事情,如下所示:
__getattr__
和__getattribute__
等魔术方法来更改。)到目前为止这么简单,并不是对绑定方法的真正解释。我只是想确保我们谈论同样的事情。缺少的部分是描述符。 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完成了一些簿记)。