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

Sea*_*ver 10 ruby python metaprogramming metaclass

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

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

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

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

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

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

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

Jör*_*tag 23

Ruby没有元类.在Ruby中有一些构造,有些人有时会错误地称为元类,但它们不是(这是无休止的混淆的来源).

但是,有很多方法可以在Ruby中实现与使用元类相同的结果.但是,如果没有告诉我们您想要做什么,那就不清楚这些机制可能是什么.

简而言之:

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

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

课程......

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

例如,Array该类生成数组对象,定义数组的行为并定义"array-ness"的含义.

回到元类.

元类......

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

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

  • Class类创建类和定义的行为一点点
  • 单个类的本征类定义了类的一些行为
  • "classness"的概念被硬连接到解释器中,它也实现了大部分行为(例如,你不能继承Class创建一种新的类,它以不同的方式查找方法,或类似的东西 - 方法查找算法是硬连线到口译员)

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

不幸的是,有些人称之为元类的特征类.(直到最近,我才成为那些被误导的灵魂之一,直到我终于看到了它.)其他人称之为所有特征类元类.(不幸的是,其中的一个人是一个Ruby的元编程和Ruby对象模型最流行的教程的作者.)一些流行的库添加metaclass到方法Object返回对象的eigenclass(如的ActiveSupport,刻面,metaid).有些人称所有虚拟类(即本征类和包含类)元类.有些人称之为Class元类.即使在Ruby源代码本身中,单词"metaclass"也用于指代不是元类的东西.

  • 自从我最初回答以来,这个问题已经发生了变化,但是我决定创建一个新的答案,而不是编辑这个答案,并保留这个答案,主要是希望在搜索"ruby"之后对这里的人有用. python元类"或类似的东西. (2认同)

Jör*_*tag 12

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

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

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

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

所以,为了完全创建一个新对象,你需要做的是:

x = X.allocate
x.initialize
Run Code Online (Sandbox Code Playgroud)

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

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

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

    return obj
  end
end
Run Code Online (Sandbox Code Playgroud)

[注意:实际上,它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__大致相当于两个newallocate在Ruby中,并__init__ 精确地对应于initialize在Ruby中.主要的区别是在Ruby中,new调用initialize而在Python中,运行时会自动调用__init__之后__new__.

例如,这是一个只允许创建最多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!
Run Code Online (Sandbox Code Playgroud)