什么是Ruby与Python元类的模拟?

时间:2010-04-20 14:39:54

标签: python ruby metaprogramming metaclass

Python具有元类的概念,如果我理解正确,允许您在构造时修改类的对象。您没有修改类,而是要创建的对象然后进行初始化。

Python(至少从3.0开始,我相信)也有类装饰器的想法。如果我理解正确的话,类装饰器允许在声明它时修改类定义。

现在我相信Ruby中的类装饰器有一个相同的特性或功能,但我目前还没有意识到类似于元类的东西。我确信你可以通过一些函数轻松地抽取任何Ruby对象并按照你的意愿去做,但是语言中是否有一个像metaclass那样设置的功能?

再说一遍,Ruby是否有类似于Python的元类的东西?

编辑我关闭了Python的元类。元类和类装饰器做的事情非常类似。它们都在定义时以不同的方式修改类。希望Python大师能够在Python中更好地解释这些功能。

但是类或类的父类可以实现__new__(cls[,..])函数,该函数在使用__init__(self[,..])初始化之前自定义对象的构造。

编辑此问题主要用于讨论和了解两种语言在这些功能中的比较。我熟悉Python但不熟悉Ruby并且很好奇。希望其他对这两种语言有相同问题的人会发现这篇文章很有帮助,也很有启发性。

2 个答案:

答案 0 :(得分:23)

Ruby没有元类。 Ruby中有一些构造,有些人有时会错误地调用元类,但它们不是(这是无穷无尽的混淆的源头)。

但是,有很多方法可以在Ruby中实现与使用元类相同的结果。但是,如果不告诉我们你想做什么,就不知道这些机制可能是什么。

简而言之:

  • Ruby没有元类
  • Ruby没有任何一个与Python的元类相对应的构造
  • Python可以使用元类执行的所有操作也可以在Ruby中完成
  • 但是没有单个构造,你将使用不同的构造,具体取决于你想要做什么
  • 这些构造中的任何一个都可能具有与元类不对应的其他特性(尽管它们可能对应于Python中的 else
  • 虽然您可以在Ruby中使用Python中的元类进行任何操作,但它可能不一定是直截了当的
  • 虽然通常会有更多的 优雅
  • 的Rubyish解决方案
  • 最后但并非最不重要:虽然您可以在Ruby中执行任何可以使用Python中的元类执行的操作,但执行它可能不一定是The Ruby Way

那么,究竟是什么元类?嗯,他们是班级。那么,让我们退后一步:究竟是什么?

课程......

  • 是对象工厂
  • 定义对象的行为
  • 在形而上学层面上定义成为班级实例的意义

例如,Array类生成数组对象,定义数组的行为并定义“array-ness”的含义。

回到元类。

元类......

  • 是班级工厂
  • 定义类的行为
  • 在形而上学层面上定义成为一个阶级意味着什么

在Ruby中,这三个职责分为三个不同的地方:

  • Class类创建类并定义一些行为
  • 个别类的本征类定义了类
  • 的一些行为
  • “classness”的概念被硬连线到解释器中,它也实现了大部分行为(例如,你不能从Class继承来创建一种新方法,以不同的方式查找方法,或者类似的东西 - 方法查找算法硬连线到解释器中)

所以,这三个东西一起扮演元类的角色,但这些都不是元类(每个都只实现元类所做的一小部分), sum 也不是那些元类(因为它们做的远不止于此)。

不幸的是,有些人称类的特殊类是元类。 (直到最近,我才成为那些被误导的灵魂之一,直到我终于看到光明。)其他人称之为所有特征类元类。 (不幸的是,其中一个人是关于Ruby元编程和Ruby对象模型的最受欢迎的教程之一。)一些流行的库向metaclass添加Object方法,返回对象的本征类(例如ActiveSupport,Facets,metaid)。有些人称​​所有虚拟类(即本征类和包含类)元类。有些人称Class为元类。即使在Ruby源代码本身中,“metaclass”一词也用于指代不是元类的东西。

答案 1 :(得分:12)

您的更新问题现在看起来很不一样了。如果我理解正确,你想要挂钩对象分配和初始化,这绝对没有任何与元类有关。 (但你不写你真正想做的事情,所以我可能仍然会离开。)

在一些面向对象的语言中,对象由构造函数创建。但是,Ruby没有构造函数。构造函数只是工厂方法(有愚蠢的限制);如果您可以使用(更强大的)工厂方法,则没有理由将它们用于精心设计的语言中。

Ruby中的对象构造如下:对象构造分为两个阶段,分配初始化。分配由名为allocate的公共类方法完成,该方法被定义为类Class的实例方法,并且通常从不覆盖。 (事实上​​,我不认为你实际上可以覆盖它。)它只是为对象分配内存空间并设置几个指针,但是,此时对象并不真正可用

这就是初始化器的用武之地:它是一个名为initialize的实例方法,它设置对象的内部状态并将其置于一个完全定义的一致状态,可供其他对象使用。

因此,为了完全创建一个新对象,您需要做的是:

x = X.allocate
x.initialize

[注意:Objective-C程序员可能会认识到这一点。]

但是,因为很容易忘记调用initialize并且作为一般规则,对象在构造之后应该是完全有效的,有一个称为Class#new的便利工厂方法,它可以完成所有这些工作。为你工作,看起来像这样:

class Class
  def new(*args, &block)
    obj = allocate
    obj.initialize(*args, &block)

    return obj
  end
end

[注意:实际上,initialize是私有的,因此必须使用反射来绕过这样的访问限制:obj.send(:initialize, *args, &block)]

顺便说一下,这就是构建一个你调用公共类方法Foo.new的对象的原因,但你实现私有实例方法{ {1}},这似乎惹恼了很多新人。

然而, none 的任何方式都以这种语言为基础。任何类的主要工厂方法通常称为Foo#initialize这一事实只是一种约定(有时我希望它不同,因为它看起来类似于Java中的构造函数,但完全不同)。在其他语言中,构造函数必须具有特定名称。在Java中,它必须与类具有相同的名称,这意味着a)只能有一个构造函数,而b)匿名类不能有构造函数,因为它们没有名称。在Python中,工厂方法必须被称为new,这意味着只能有一个。{1}}。 (在Java和Python中,你当然可以使用不同的工厂方法,但是调用它们看起来与调用默认值不同,而在Ruby中(和Smalltalk从这个模式发起)它看起来是一样的。)

在Ruby中,可以有任意数量的工厂方法,您可以使用任何名称,工厂方法可以有许多不同的名称。 (例如,对于集合类,工厂方法通常是__new__的别名,这允许您编写[]而不是List[1, 2, 3],它们看起来更像一个数组,从而强调集合列表的性质。)

简而言之:

  • 标准化的工厂方法是List.new(1, 2, 3),但它可以是任何
  • Foo.new调用Foo.new为空对象分配内存allocate
  • foo然后调用Foo.new,即foo.initialize实例方法
  • 所有这些都只是与其他方法一样的方法,您可以取消定义,重新定义,覆盖,换行,别名等等
  • 好吧,除了需要在Ruby运行时内分配内存的Foo#initialize,你无法从Ruby真正做到这一点

在Python中,allocate大致对应Ruby中的__new__newallocate 完全对应__init__在Ruby中。主要区别在于,在Ruby中,initialize调用new而在Python中,运行时会在initialize之后自动调用__init__

例如,这是一个只允许创建最多2个实例的类:

__new__