Ruby 元类混淆

Posted

技术标签:

【中文标题】Ruby 元类混淆【英文标题】:Ruby metaclass confusion 【发布时间】:2012-05-18 11:56:18 【问题描述】:

我了解 ruby​​ 中的所有类都是元类 Class 的实例。而“常规”对象是这些类的实例(元类类的实例)。

但我一直在想,我的意思是类是对象的根,类本身就是 Class 的实例(称为元类,因为它的实例是类)。我在一些博客中看到了 Class 类的方法 new 的一些覆盖。

所以 Class 表现得像一个类,但它的实例是类。所以看起来我们有一个圆圈,看起来类 Class 是它自己的一个实例。

我显然在这里遗漏了一点。类Class的由来是什么?

这是一个让我困惑的例子:

class Class
  def new
    #something
  end
end

但是关键字class 暗示了类Class 的一个实例。那么这是如何工作的呢?

【问题讨论】:

Class.class # => Class 一路下来都是乌龟! 另见The class/object paradox confusion。 【参考方案1】:

这是如何工作的

简单:它没有。无论如何,不​​在 Ruby 中。

就像在大多数其他语言中一样,有一些核心实体被简单地假设为存在。它们从天而降,凭空显现,神奇地出现。

在 Ruby 中,其中一些神奇的东西是:

Object 没有超类,但不能定义没有超类的类,隐式直接超类始终为Object。 [注意:Object 可能存在实现定义的超类,但最终会有一个没有超类。] ObjectClass 的一个实例,它是Object 的子类(这意味着间接地ObjectObject 本身的一个实例) ClassModule 的子类,Class 的实例 ClassClass 的一个实例

这些东西都不能用 Ruby 解释。

BasicObjectObjectModuleClass 都需要同时出现,因为它们具有循环依赖关系。

仅仅因为这种关系不能用 Ruby 代码表达,并不意味着 Ruby 语言规范不能说它必须如此。由实现者来想办法做到这一点。毕竟,Ruby 实现对您作为程序员所没有的对象具有一定的访问权限。

例如,Ruby 实现可以首先创建BasicObject,将其superclass 指针和class 指针都设置为null

然后,它创建Object,将其superclass 指针设置为BasicObject,并将其class 指针设置为null

接下来,它创建Module,将其superclass 指针设置为Object,并将其class 指针设置为null

最后,它创建Class,将其superclass 指针设置为Module,并将其class 指针设置为null

现在,我们可以覆盖BasicObject's、Object's、Module's 和Class's class 指向Class 的指针,我们就完成了。

这在系统外部很容易做到,只是从内部看起来很奇怪。

然而,一旦它们确实存在,完全有可能在纯 Ruby 中实现它们的大部分行为。您只需要这些类的非常简单的版本,感谢 Ruby 的开放类,您可以在以后添加任何缺少的功能。

在您的示例中,class Class 并未创建名为 Class 的新类,而是重新打开运行时环境提供给我们的 现有Class

因此,完全可以用普通的 Ruby 来解释 Class#new 的默认行为:

class Class
  def new(*args, &block)
    obj = allocate # another magic thing that cannot be explained in Ruby
    obj.initialize(*args, &block)
    return obj
  end
end

[注意:实际上initialize是私有的,所以你需要使用obj.send(:initialize, *args, &block)来绕过访问限制。]

顺便说一句:Class#allocate 是另一个神奇的东西。它在 Ruby 的对象空间中分配一个新的空对象,这是 Ruby 无法做到的。因此,Class#allocate 也必须由运行时系统提供。

【讨论】:

我想你的意思是“BasicObject 的超类是nil”,因为Object 的超类是BasicObject @AndrewMarshall:这是 1.9 中的新功能。 1.8 只有Object 没有BasicObject @HolgerJust 是的,但 1.8 已接近使用寿命,除非询问者指定,否则我假设是最新版本的 Ruby。 技术上,Ruby 语言规范并没有说Objectdirect 超类是nil,只是说最终 是@ 之一987654380@ 的祖先的超类是nil。这样,Ruby 1.9 的 BasicObject 或 MacRuby 的 NSObject 仍然符合规范。 方法超类返回类的超类,或nil。这并不意味着 nil 是超一流的......【参考方案2】:

“twist”链接给出了一个元循环。它是从根的特征类到Class 类的内置超类链接。这可以表示为

BasicObject.singleton_class.superclass == Class

了解.class 映射的线索是将此映射视为从特征类和超类链接派生的:对于对象xx.classx 的特征类的超类链中的第一个类.这可以表示为

x.class == x.eigenclass.superclass(n)

其中eigenclasssingleton_class 的“概念别名” (抵抗立即值问题),y.superclass(i) 表示 i-y 的超类,n 是最小的,因此 x.eigenclass.superclass(n) 是一个类。等效地,x.eigenclass 的超类链中的特征类被跳过(参见rb_class_real,它还揭示了在 MRI 中,甚至superclass 链接也是间接实现的——它们是通过跳过“iclasses”而产生的)。 这导致每个类(以及每个特征类)的class 始终是Class 类。

图片由this diagram提供。

元类混淆有两个主要来源:

闲聊。 Smalltalk-80 对象模型包含由 Ruby 对象模型纠正的概念上的不一致。此外,Smalltalk 文献在术语中使用辩证法,不幸的是,Ruby 文献中没有充分弥补这一点。

元类的定义。目前,该定义指出元类是类的类。然而,对于所谓的“隐式元类”(Ruby 和 Smalltalk-80 的情况),更合适的定义是类的元对象。 p>

【讨论】:

【参考方案3】:

是的,Class 是它自己的一个实例。它是Module的子类,也是类的实例,Module是Object的子类,Object也是Class的实例。它确实是循环的——但这是核心语言的一部分,而不是库中的东西。当我们编写 Ruby 代码时,Ruby 运行时本身没有你或我所做的相同限制。

不过,我从来没有听说过“元类”这个词用来谈论 Class。它在 Ruby 中根本没有使用太多,但当它使用时,它通常是官方称为“对象的单例类”的同义词,这比对象-模块-类三角形更令人困惑。

【讨论】:

根据一般定义,类确实可以称为元类。但是许多红宝石学家错误地将世界元类用于其他事物。但是根据一般定义,这些元类是弱元类。如果我记得很清楚,ruby 中通常称为元类的东西是持有类方法的单例。我读过几篇关于它的帖子,说元类这个词被错误地使用了,他们在这一点上指责 Rails 团队。谢谢大家。【参考方案4】:

虽然它有点过时,但this article by _why 可能有助于理解这种行为。您可以在 Paolo Perrotta 的Metaprogramming Ruby 中找到更深入的主题。

【讨论】:

What Why 与 Parello 对“元类”一词的使用无关。他说的是单例类,或者更通俗地说,特征类。

以上是关于Ruby 元类混淆的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 元类疯狂

Ruby 与 Python 元类的类比是啥?

给定一个 Ruby 对象的实例,我如何获得它的元类?

Ruby - 在 ruby​​ 类的元类中定义 self 方法

Python 的元类设计起源自哪里?

Python 的元类设计起源自哪里?