Ruby 与 Python 元类的类比是啥?

Posted

技术标签:

【中文标题】Ruby 与 Python 元类的类比是啥?【英文标题】:What is Ruby's analog to Python Metaclasses?Ruby 与 Python 元类的类比是什么? 【发布时间】:2011-02-10 04:27:24 【问题描述】:

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

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

现在我相信 Ruby 中的类装饰器有一个或多个等效功能,但我目前不知道与元类等效的东西。我敢肯定,您可以通过一些函数轻松地抽取任何 Ruby 对象并对其执行任何操作,但是该语言中是否有像元类一样设置它的功能?

那么,Ruby 是否有类似于 Python 的元类的东西?

编辑 我对 Python 的元类很感兴趣。元类和类装饰器做的事情看起来非常相似。它们都在定义类时修改类,但方式不同。希望 Python 大师能进来并更好地解释 Python 中的这些功能。

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

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

【问题讨论】:

嗯...我可能仍然遗漏了一些东西,但是在阅读了几个链接之后,它开始听起来像 Python 的元类和 Ruby 的元类是不同的。我希望有机会做更多的研究并发布我所学到的东西。当然,除非有人知道。链接:Katz on Metaclasses、Metaclass and Ruby internals 【参考方案1】:

Ruby 没有元类。 Ruby 中有一些结构,有些人有时会错误地将元类称为元类,但实际上并非如此(这是无尽混乱的根源)。

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

简而言之:

Ruby 没有元类 Ruby 没有任何一种结构与 Python 的元类相对应 Python 可以对元类执行的所有操作也可以在 Ruby 中完成 但是没有单一的构造,你将根据你想要做什么使用不同的构造 这些构造中的任何一个都可能具有其他不对应于元类的特性(尽管它们可能对应于 Python 中的某些东西else) 虽然您可以在 Ruby 中做任何可以在 Python 中使用元类做的事情,但它可能并不一定很简单 虽然通常会有一个更 Rubyish 的解决方案 优雅 最后但并非最不重要的一点:虽然您可以在 Ruby 中做任何可以在 Python 中使用元类做的事情,但这样做可能不一定是 Ruby 方式

那么,究竟什么是元类?嗯,它们是类的类。那么,让我们退后一步:到底是什么?

类……

是对象的工厂 定义对象的行为 在形而上学的层面上定义成为类的实例意味着什么

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

回到元类。

元类……

是类的工厂 定义类的行为 在形而上学的层面上定义什么是类

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

Class 类创建类并定义了一些行为 单个类的 eigenclass 定义了该类的一些行为 “类”的概念被硬连线到解释器中,它还实现了大部分行为(例如,您不能从Class 继承来创建一种以不同方式查找方法的新类,或者类似的东西那——方法查找算法被硬连线到解释器中)

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

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

【讨论】:

感谢您的帖子。这基本上正是我想要的。现在希望有更多 Python 知识的人可以评论或发布有关各种 Python 功能的信息。 自从我最初回答以来问题已经改变,但我决定创建一个新答案而不是编辑这个答案,并留下这个,主要是希望它对结束的人有用在搜索“ruby python metaclass”或类似的东西之后在这里。 感谢您的回答。这个问题的目的是了解两种语言的元类。当我意识到我在 Python 中对它们的理解是错误的时,我添加了第一个编辑。对不起,如果这让你感到困惑。您的两个答案都对了解 Ruby 的功能非常有帮助。【参考方案2】:

您更新后的问题现在看起来完全不同了。如果我理解正确的话,你想加入对象分配和初始化,这与元类完全无关。 (但你仍然没有写出你真正想做的事情,所以我可能还是会离开。)

在一些面向对象的语言中,对象是由构造函数创建的。但是,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,但你实现一个私有实例方法Foo#initialize ,这似乎绊倒了很多新人。

但是,没有这一切都以任何方式融入到语言中。任何类的主要工厂方法通常称为 new 的事实只是一种约定(有时我希望它有所不同,因为它看起来类似于 Java 中的构造函数,但完全不同) .在其他语言中,构造函数必须具有特定名称。在 Java 中,它必须与类同名,这意味着 a) 只能有一个构造函数,b) 匿名类不能有构造函数,因为它们没有名称。在 Python 中,必须调用工厂方法__new__,这再次意味着只能有一个。 (在 Java 和 Python 中,您当然可以有不同的工厂方法,但调用它们看起来与调用默认值不同,而在 Ruby(以及该模式起源的 Smalltalk)中看起来完全一样。)

在 Ruby 中,可以有任意多的工厂方法,您可以使用任何您喜欢的名称,并且工厂方法可以有许多不同的名称。 (例如,对于集合类,工厂方法通常别名为[],它允许您编写List[1, 2, 3] 而不是List.new(1, 2, 3),后者看起来更像一个数组,因此强调了列表的集合性质。 )

简而言之:

标准化的工厂方法是Foo.new,但它可以是任何东西 Foo.new 调用 allocate 为空对象 foo 分配内存 Foo.new 然后调用foo.initialize,即Foo#initialize 实例方法 所有这三个方法都与其他方法一样,您可以取消定义、重新定义、覆盖、包装、别名等等 好吧,除了 allocate 需要在 Ruby 运行时内分配内存,而这在 Ruby 中是无法做到的

在 Python 中,__new__ 大致对应于 Ruby 中的 newallocate,而__init__ 恰好 对应于 Ruby 中的 initialize。主要区别在于,在 Ruby 中,new 调用 initialize,而在 Python 中,runtime__new__ 之后自动调用 __init__

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

class Foo
  def self.new(*args, &block)
    @instances ||= 0
    raise 'Too many instances!' if @instances >= 2

    obj = allocate
    obj.send(:initialize, *args, &block)

    @instances += 1

    return obj
  end

  attr_reader :name

  def initialize(name)
    @name = name
  end
end

one = Foo.new('#1')
two = Foo.new('#2')
puts two.name         # => #2
three = Foo.new('#3') # => RuntimeError: Too many instances!

【讨论】:

你不需要obj = super 吗? :) @banister:是的,你说得对。我在最后一分钟切换了它,以确保在对象实际成功构造并忘记测试它之前我不会增加@instances。我只是将其切换回来,因为对于这么少的示例代码来说,这可能是矫枉过正。在实际系统中,您可能会将其放入 else 异常块中。 @banister:实际上,整个事情都经过深思熟虑。

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

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

django 模型元类的标准文档字符串是啥?

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法

python 面向对象专题:元类type反射函数与类的区别特殊的双下方法

Python 中的元类是啥?

Python 中的元类是啥?