在过去几周内,我对 Ruby 对象模型进行了深入的研究。以前我只是使用 Ruby 和 Python 对象模型的成果,而现在我很好奇这些东西在其他语言中的实现会有何不同。多年前,我接触过 Smalltalk 的 Squeak 版本。Smalltalk 通常被认为是一种参考性的面向对象语言,这也是我对它感兴趣的原因。Ruby 对象模型不区分对象和类,存在单例对象使每个对象成为了一个原型,必要时可进行扩展。据我所知,所有这些都是作为元对象协议定义的。这些特性让编程比 C++ 或 Java 等其他不太动态或非动态语言更加轻松。
请给出一个概念,说明动态语言,特别是 Smalltalk、Python 和 Ruby 在实现元对象协议方面的差异是什么?
- 解决方案
Python 和 Smalltalk 之间的主要区别在于属性隐私处理方式。在 Smalltalk 中,我定义了属性,并必须立即生成所有访问器(幸运的是,Dolphin Smalltalk 做到了这一点)并使用它们。另一方面,在 Python 中,一切都可访问,即使是那些被认为是私有的属性(以 __ 开头,被破坏为 ___)。有些人可能会说这是潜在危险的——例如,在未来的某个时刻,你需要在更改某些属性时执行某些操作。但 Python 通过属性很好地解决了这个问题。
我喜欢我可以访问我想要的一切的想法。如果我只知道自己在做什么,我就可以做到 😃
在 Python 中,每个对象都有一个命名空间——对象的“属性”,可在此命名空间中访问,可以是方法、对其他对象(可调用或不可调用)的简单引用,或者由描述符动态生成(普通属性可能存在于实例或类中,但描述符——包括用作描述符的函数,用于合成方法——仅在存在于类中时才使用,而不是实例中;因此,尤其是“特殊方法”仅当在类中定义时才特殊,而不是在实例中)。属性处理内置函数和特殊方法(getattr、setattr、getattr、setattr 等)对于对象的单个命名空间以相同的方式工作,无论引用的是“普通属性”还是不是。
关键在于,对于任何对象 a,在 Python 中,a.b 可以是方法(或其他可调用对象)或不是:Python 编译器不知道(也不关心),你可以获取引用 a.b 并(例如)将其作为参数传递,将其作为结果返回,将其附加到列表中,等等,这些操作都不意味着调用 a.b。如果你想(尝试)调用 a.b,你可以通过后缀括号显式地这样做:a.b(),如果你想在没有参数的情况下调用它。
在 Ruby 中,对象的函数和属性存在于不同的命名空间中(因此你可以拥有一个具有函数和属性(同名)的对象,在 Python 中你无法拥有),并且“仅仅提到”一个无参数函数会隐式地调用它(因此 c=a.b 可能只是获取属性引用,或者可能调用 a.b();如果 b 在 a 中同时命名了一个函数和一个属性,我不记得使用了什么启发式规则来消除歧义)。因此,如果你只想获取函数引用(例如,存储在某个容器中或用作参数或返回值),稍后执行调用,你应该使用不同的语法。
Smalltalk 也有单独的命名空间,就像 Ruby 一样,但你无法引用给定对象的“非函数”属性(每个对象只能“看到”自己的属性),因此不会出现这种歧义(但你仍然必须使用特定的消息来提取并稍后调用“函数引用”)。