【问题标题】:What are the *actual* steps in ruby's method lookup?ruby 方法查找中的*实际*步骤是什么?
【发布时间】:2018-05-11 07:12:55
【问题描述】:

我已阅读有关此主题的 stackoverflow 帖子以及包括 A Primer on Ruby Method LookupWhat is the method lookup path in Ruby 在内的几篇文章。另外,我查看了Ruby Metaprogramming 2中的对象模型章节,在几个聊天室中询问,并发了this reddit thread。由于没有学习 C,我已经尽我所能来解决这个问题。

如上述资源所述,这 6 个位置在对接收对象(如fido_instance)进行方法查找期间被检查(按顺序):

  1. fido_instance 的单例类
  2. IClass(来自扩展模块)
  3. IClass(来自前置模块)
  4. IClass(来自包含的模块)
  5. 超类(如果此处未找到方法,请重复步骤 4-6)

显然,图表是不完整的,所有这些单例类可能都不是在现实世界中创建的。尽管如此,这 6 个步骤仍有很多不足之处,并且不包括以下场景。如果在fido_instance 的单例类之上没有扩展/前置IClass,则没有说明步骤4 是否在fido_instance 的单例类上执行。我必须假设不会,因为整个方法查找会短路。

如果我猜测一组可以解释 ruby​​ 的方法查找行为的步骤,它可能看起来像:

  1. 检查fido_instance.class 的方法。 (显然,ruby 不会使用自己的#class 方法来进行方法查找,但它传达了过程的逻辑)
  2. 检查fido_instance.class.superclass 的方法。继续添加.superclass 并检查该方法,直到没有剩余超类。 (同样,ruby 不会使用自己的#superclass 方法)
  3. 未找到方法。从第 1 步开始,这次寻找#method_missing。

我还记得读过,如果接收对象是一个类,则有一个单独的方法查找过程,但我不记得在哪里。

那么,不涉及了解 C 的正确、详细的解释是什么?

【问题讨论】:

  • 从实例的角度来看,它本质上是 singleton_method -> 前置模块方法 -> 实例方法 -> 包含模块方法 -> 超类实例方法(递归到基本对象)

标签: ruby object-model


【解决方案1】:

在第二个参考文献中有一个...宝石...我认为它触及了答案的核心:单例类的祖先。应用于您的对象,它将是:

fido_instance.singleton_class.ancestors

这将始终为您提供 Ruby 使用的方法查找顺序。当您以这种方式查看它时,它非常简单,这就是您问题的底线答案。 Ruby 将从singleton_class 开始,然后沿着祖先寻找该方法。使用您的图表:

fido.singleton_class.ancestors
=> [Fetch, WagTail, DogClass, Object, Kernel, BasicObject]

(注意 1:Bark 不是此输出的一部分,因为您使用的是 extend 而不是 include。稍后会详细介绍。)

(注2:如果一直到BasicObject都没有找到,那么它会在同一个祖先链上调用method_missing。)

在类上调用方法时没有什么不同,因为在 Ruby 中,类只是类Class 的一个实例。所以DogClass.method1 会像以前一样在DogClass.singleton_class 上搜索method1,然后沿着它的祖先链向上搜索。

DogClass.singleton_class.ancestors
=> [Bark, Class, Module, Object, Kernel, BasicObject]

由于您将extend 用于Bark,因此我们可以在这里找到它!因此,如果Bark 定义了一个方法bark,那么您可以调用DogClass.bark,因为该方法是在DogClass 的singleton_class' 祖先中定义的。

要了解祖先树是什么(而不是每次都打印出来),您只需要知道祖先是如何通过子类化修改的,extendincludeprepend 等。

  1. 子类为子类提供其超类的整个祖先链。
  2. includeC 类中的一个模块将该模块添加到 C 之后和其他所有内容之前的祖先链中。
  3. prepend在类中添加一个模块 C 将该模块添加到祖先链中的所有内容之前,包括 C 和任何当前前置的模块。
  4. def x.method1method1 添加到 x.singleton_class。同样,x.extend(M) 会将M 添加到x.singleton_class 的祖先中(但不会添加到x.class)。请注意,后者正是 BarkDogClass.singleton_class 发生的情况,但同样适用于任何对象。

从上面的列表中省略extend,因为它不会修改对象的祖先链。它确实修改了该对象的singleton_class 的祖先——正如我们所见,Bark 包含在DogClass.singleton_class.ancestors 中。


切线:

上面关于类方法的部分是我理解单例类对 Ruby 的重要性的关键。你显然不能在DogClass.class 上定义bark,因为DogClass.class == Class 而我们不想在Class 上定义bark!那么我们如何让DogClass 成为Class 的一个实例,允许它拥有一个为bark 定义的(类)方法bark,但不是不相关的类?使用单例类!这样,定义一个“类方法”,比如在类C 中通过def self.x,有点像C.singleton_class.send(:define_method, :x) {...}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-19
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 2017-05-17
    • 1970-01-01
    • 2010-12-07
    相关资源
    最近更新 更多