草庐IT

ruby - ruby 方法查找中的*实际*步骤是什么?

coder 2025-07-15 原文

我已经阅读了有关此主题的 stackoverflow 帖子以及包括 A Primer on Ruby Method Lookup 在内的几篇文章, What 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. 父类(super class)(如果这里没有找到方法,重复步骤 4-6)

显然,该图并不完整,所有这些单例类可能都不是在现实世界中创建的。尽管如此,这 6 个步骤仍有很多不足之处,并且不涵盖以下场景。如果fido_instance单例类之上没有extended/prepended IClass,那么就没有说明第4步是否在fido_instance单例类上执行。我不得不假设不会,因为整个方法查找会短路。

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

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

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

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

最佳答案

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

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]

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

要了解祖先树是什么(而不是每次都打印出来),您只需要知道祖先是如何通过子类化、extendinclude前置

  1. 子类化为子类提供了其父类(super class)的整个祖先链。
  2. includeC 中的模块将该模块添加到祖先链中 C 之后和其他所有内容之前。
  3. prependC 中的模块将该模块添加到祖先链中的所有内容之前,包括 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 而我们不想要 bark Class 上!那么我们如何才能让 DogClass 成为 Class 的一个实例,让它拥有一个为 定义的(类)方法 bark >DogClass 但不是不相关的类?使用单例类!通过这种方式,在类 C 中通过 def self.x 定义“类方法”有点像 C.singleton_class.send(:define_method , :x) {...}.

关于ruby - ruby 方法查找中的*实际*步骤是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50286933/

有关ruby - ruby 方法查找中的*实际*步骤是什么?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  4. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  5. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  6. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  7. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

    这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

  8. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  9. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  10. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

随机推荐