关于全局/范围的 Ruby 方法查找

2024-02-08

我试图完全理解 Ruby 如何定位方法/符号,但当它涉及多个级别时,尤其是全局/文件范围,我感到很困难。

当在类上显式调用方法时,有很多关于搜索类及其包含的模块的顺序的说明(因此确切地说是什么super每种情况下都会调用)。但是当没有显式调用方法时,例如一个平原func args而不是self.func args搜索顺序是什么?

为什么在我下面的示例中,成员方法调用func找到全局之前的成员方法,但是func2找到全局没有method_missing被召唤? 当全局是模块/类/类型时,为什么根本找不到同名的成员?

是否有任何官方文档说明该语言在遇到“时到底会做什么”func, func(), func arg“等等在一个方法中?有很多第三方博客,但他们只真正讨论了单个实例include Module and class Type < BaseType.

def func; "global func" end
def func2; "global func 2" end

class Test
  def x; func end
  def y; func2 end
  def z; Math end
  def w
    func = "local_var"
    [func(), func]
  end


  def func(arg=nil); "method func" end
  def func=(x); puts "assign func=" end
  def Math; "method Math" end

  def method_missing(sym, *args, &block)
    puts "method_missing #{sym}"
    super(sym, *args, &block)
  end
end

x = Test.new
puts x.x.inspect # "method func", member overrides global
puts x.y.inspect # "global func 2", "method_missing" was not called
puts x.z.inspect # "Math" module, member did not override global
puts x.w.inspect # ["method_func", "local_var"], local variables are always considered before anything else

Ruby的方法查找算法实际上是really simple:

  • 检索class接收者指针
  • 如果该方法存在,则调用它
  • 否则检索superclass指针,然后重复

就是这样。

如果算法到达没有更多超类的地步,但它仍然没有找到该方法,它将再次重新启动整个过程,method_missing作为消息和原始消息的名称添加到参数之前。但仅此而已。这就是整个算法。它非常小而且非常简单,而且它has非常小且非常简单,因为方法查找是面向对象语言中最常执行的操作。

注:我完全无视Module#prepend http://ruby-doc.org/core/Module.html#method-i-prepend / Module#prepend_features http://ruby-doc.org/core/Module.html#method-i-prepend_features因为我对它是如何工作的还不够了解。我只知道它的作用,这对我来说就足够了。

另请注意:我忽略了性能优化,例如在多态内联缓存等中缓存方法查找的结果。

好的,但是这里有一个技巧:这些到底在哪里class and superclass指针指向?嗯,他们确实not指出什么Object#class http://ruby-doc.org/core/Object.html#method-i-class and Class#superclass http://ruby-doc.org/core/Class.html#method-i-superclass方法返回。那么,让我们退后一步。

每个对象都有一个class指向对象的类的指针。并且每个班级都有一个superclass指向其超类的指针。

让我们开始一个运行示例:

class Foo; end

现在,我们上课了Foo,及其superclass指针指向Object.

foo = Foo.new

还有我们的对象foo's class指针指向Foo.

def foo.bar; end

现在事情开始变得有趣了。我们创建了一个单例方法。嗯,实际上,不存在单例方法这样的东西,它实际上只是一个普通的方法。单例类。那么,这是如何运作的呢?好吧,现在class指针指向foo的单例类和foo的单例类superclass指针指向Foo!换句话说,单例类被插入到两者之间foo及其“真实”类别Foo.

然而,当我们问foo关于它的类,它仍然响应Foo:

foo.class #=> Foo

The Object#class方法知道单例类,并且简单地跳过它们,遵循superclass指针,直到找到“正常”类并返回该类。

下一个并发症:

module Bar; end

class Foo
  include Bar
end

这里会发生什么? Ruby 创建了一个new类(我们称之为Barʹ),称为包括类。该类的方法表指针、类变量表指针、常量表指针指向Bar的方法表、类变量表、常量表。然后,Ruby 使Barʹ's superclass指针指向Foo的当前超类,然后使Foo的超类指针指向Barʹ。换句话说,包含模块会创建一个新类,该新类将作为包含该模块的类的超类插入。

这里有一点复杂:你也可以include模块到模块。这是如何运作的?好吧,Ruby 只是跟踪包含在模块中的模块。然后,当模块被包含到类中时,它将为每个包含的模块递归地重复上述步骤。

这就是您需要了解的有关 Ruby 方法查找的全部内容:

  • 找到班级
  • 跟随超类
  • 单例类插入上面的对象
  • 包含类插入上面的类

现在我们来看看您的一些问题:

当在类上显式调用方法时,有很多关于搜索类及其包含的模块的顺序的说明(因此确切地说是什么super每种情况下都会调用)。但是当没有显式调用方法时,例如一个平原func args而不是self.func args搜索顺序是什么?

相同。self是隐式接收者,如果不指定接收者,则接收者是self。并且括号是可选的。换句话说:

func args

is 一模一样 as

self.func(args)

为什么在我下面的示例中,成员方法调用func找到全局之前的成员方法,但是func2找到全局没有method_missing被召唤?

Ruby 中不存在“全局方法”这样的东西。也没有“成员方法”这样的东西。每个方法都是一个实例方法。时期。没有全局、静态、类、单例、成员方法、过程、函数或子例程。

在顶层定义的方法成为private类的实例方法Object. Test继承自Object。运行我上面概述的步骤,您将确切地发现发生了什么:

  • 取回x的类指针:Test
  • Does Test有一个方法叫做func: 是的,所以调用它。

现在再说一遍:

  • 取回x的类指针:Test
  • Does Test有一个方法叫做func2: No!
  • 取回Test的超类指针:Object
  • Does Object有一个方法叫做func2: 是的,所以调用它。

当全局是模块/类/类型时,为什么根本找不到同名的成员?

再说一次,这里没有全局,这里没有成员。这也与模块或类没有任何关系。而且 Ruby 没有(静态)类型。

Math

是对常量的引用。如果你想调用一个同名的方法,你必须确保 Ruby 可以知道它是一个方法。有两件事only方法可以有:接收者和参数。因此,您可以添加接收器:

self.Math

或参数:

Math()

现在 Ruby 知道你指的是方法Math而不是常数Math.

顺便说一句,这同样适用于局部变量。和二传手。如果你想调用 setter 而不是分配局部变量,你需要说

self.func = 'setter method'
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于全局/范围的 Ruby 方法查找 的相关文章

  • 将 ruby​​ 类转换为模块比使用改进更好的方法?

    Module refine http ruby doc org core 2 0 0 Module html method i refine方法接受一个类和一个块并返回一个细化模块 所以我想我可以定义 class Class def inc
  • 回形针不支持 .doc 文件

    在 Rails 4 0 2 中 我使用回形针 gem 上传文件 但它不支持 doc 文件 在文件上传字段下方 显示一条错误消息 扩展名与其内容不匹配 在模型中 检查内容类型的验证如下 validates attachment content
  • 下载所有 gems 依赖项

    我想通过下载任何所需的文件并将它们带到另一台计算机来安装指南针没有互联网连接 我已经下载了指南针的源包 当我在未连接的计算机上运行 gem 时 它抱怨缺少依赖项 有什么解决办法吗 这正是我遇到的问题 经过一段时间的搜索后 我找到了一个可以使
  • class_eval、class_exec、module_eval 和 module_exec 之间有什么区别?

    我正在读Module文档 但似乎无法理解它们的差异以及应该在哪里使用 怎么样eval不同于exec 我将回答比你的问题更多的内容 包括instance eval exec 在你的问题中 所有变体 instance module class
  • Ruby gem 环境问题 - LoadError: no such file to load -- robots

    我正在尝试使用 anemone gem 编写一个爬虫 这需要 robots gem 不管出于什么原因 机器人绝对不会包括在内 这是我的一些环境信息 gem list d robots LOCAL GEMS robots 0 10 1 Aut
  • 随机采样数组的唯一子集

    如果我有一个数组 a 1 2 3 如何随机选择数组的子集 以使每个子集的元素都是唯一的 也就是说 对于a可能的子集是 1 2 3 1 2 2 3 1 2 3 我无法生成所有可能的子集 因为 a 的实际大小非常大 因此有很多很多子集 目前 我
  • 将“Authorization Bearer”哈希添加到 Net::HTTP post 请求 (Ruby)

    我怎样才能添加Authorization Bearer到 POST 请求Net HTTP 我只能在文档中找到 基本身份验证 的帮助 req basic auth user pass Source https docs ruby lang o
  • GIT 和 Ruby:如何从 ruby​​ 脚本内部取消设置 GIT_DIR 变量?

    我编写了一个非常简单的 部署 脚本作为我的post update挂钩到我的裸 git 存储库中 变量如下 live domain mydomain com staging domain stage mydomain com git repo
  • 如何加载 UrlHelper 和 Rails 中的路线?

    我想包括路线和link toPORO 中的方法 在控制台中测试这个时 我遇到了这个 如果我在没有路由助手的情况下包含 UrlHelper 一切似乎都工作正常 ruby 1 9 3 rc1 001 gt Rails version gt 3
  • Chef - 使用动态变量创建模板?

    我在厨师食谱上遇到了一些挑战 我是厨师新手 所以请耐心等待 第 1 步 我的厨师食谱安装 Ruby Passenger 然后与 Nginx 一起编译 Passenger nginx 模块 Install passenger and ngin
  • 未知属性:user_id

    我在执行 current user stories build 期间收到错误未知属性 user id class User lt ActiveRecord Base has many stories class name Story for
  • 正常关闭 sidekiq 进程

    有谁知道如何找到 sidekiq 的 pidfile 来优雅地关闭它 跑步ps ax grep sidekiq然后运行sidekiqctl stop
  • 添加两个 ActiveRecord::Relation 对象[重复]

    这个问题在这里已经有答案了 如何将两个关系添加在一起 当我尝试 运算符时 它返回一个数组 但我需要它来返回关系 谢谢 麦克风 Try new relation relation merge another relation
  • 由于 MIME 类型不受支持,拒绝应用样式

    我不断收到一条错误消息 指出 MIME 类型 text html 不可执行或不是受支持的样式表 MIME 类型 并且启用了严格的 MIME 检查 我的链接代码是
  • 如何从数组中提取特定元素?

    如果我有一个数组a 1 2 3 4 5 6 7 8 9 10 我想要这个数组的一个子集 第 1 个 第 5 个和第 7 个元素 是否可以通过简单的方式从该数组中提取这些内容 我在想这样的事情 a 0 4 6 1 5 7 但这行不通 还有一种
  • rake db:migrate db:reset 和 db:schema:load 之间的区别

    和 之间的不同rake db migrate and rake db reset我很清楚 我不明白的是如何rake db schema load与前两者不同 只是为了确保我在同一页面上 rake db migrate 运行尚未运行的迁移 r
  • 如何检查字符串是否为有效日期

    我有一个字符串 31 02 2010 并想检查它是否是有效日期 最好的方法是什么 我需要一个方法 如果字符串是有效日期 则返回 true 如果不是 则返回 false require date begin Date parse 31 02
  • Rails 4 可安装引擎,找不到文件“jquery”

    我正在创建一个 Rails 可安装引擎插件 它使用 gem jquery rails 我在 gemspec 文件中添加了这段代码 s add dependency jquery rails gt 3 0 1 and run bundle i
  • ArgumentError:Ruby 中的参数数量错误

    试图解决这个问题 class Person def initialize name name name end def greet other name puts Hi other name my name is name end end
  • 如何在 Ruby 中重写 require ?

    我需要覆盖require来自 Ruby 文件 这是我的 start rb 应用程序入口点 所必需的 rubygems在此之前加载 位于 start rb 中 我尝试的所有操作都出现了堆栈溢出错误 正确的做法是什么 一般来说 如果你想修补一些

随机推荐