Ruby 的方法查找再往前一步 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Mark24
V2EX    Ruby

Ruby 的方法查找再往前一步

  •  
  •   Mark24 2021-07-29 18:51:58 +08:00 3660 次点击
    这是一个创建于 1599 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有更好的方法可以告诉我,我最新在学习 Ruby

    最新的修改会更新在 BLOG

    我的博客

    RubyChina 讨论帖


    背景

    《 Ruby 元编程(第二版)》 5.4 节 单件类 在 Page125 这页,讲了一种情况:

     class C def a_method 'C#a_method()' end end class C class << self def a_class_method '#C.a_class_method() #singleton' end end end class D < C;end obj = D.new D.a_class_method # => '#C.a_class_method() #singleton' 

    D.a_class_method 他是如何查找的呢?

    本文就是寻找这个的答案。讲的是 Ruby 的方法查找再往前走一步。

    为了说明这个问题,先要嗦的做一些铺垫。

    下文中,此书简称为《元编程》

    一、Ruby 的继承结构

    这张图实在总结的太美丽了。先放在这里。图片的出处,可以参考文末。

    二、Ruby 一般方法查找规则

    《元编程》里面里面总结了 Ruby 的查找规则:

    “向右一步,然后向上查找”。

    意思就是,向右寻找他的父,然后开始往上寻找继承关系,通过这种方式查找方法。

    比如以下代码

    class C def a_method 'C#a_method()' end end class D < C;end obj = D.new obj.a_method 

    obj 如何查找 a_method 方法呢?

    如果我们给 obj 对象添加单例类,他会如何查找呢?

     class C def a_method 'C#a_method()' end end class D < C;end obj = D.new # 定义单例类 class << obj def a_singleton_method "obj#a_singleton_method" end end obj.a_singleton_method 

    obj.a_singleton_method 会如何查找方法呢?

    可以通过一下方式检验

     obj.singleton_class.superclass # => D 

    他会按照如图的方式,其实实例对象创造了一个 单例类 可以标记为 #obj,用#表示单例类。 #obj 会出现在对象和真正的类中间。

    我们也能用上面

    “向右一步,然后向上查找”。

    来指导我们查找,只不过对象存在一个单例类罢了。

    三、新的问题出现

    但是问题来了,回到文章的最开头。

     class C def a_method 'C#a_method()' end end class C class << self def a_class_method '#C.a_class_method() #singleton' end end end class D < C;end obj = D.new D.a_class_method # => '#C.a_class_method() #singleton' 

    这个例子。在类 C 上定义了单例方法,并且我们指导所有东西在 Ruby 里都是对象,都可以定义单例方法。

    这就是文章开头最先的图片。所有的类都可以定义单例类。这种情况下,D.a_class_method 应该如何查找呢?

    “向右一步,然后向上查找”。

    似乎帮不了我们了。因为我们面临一个问题,让我来描述下:

    我们把 D 当做一个对象,开始寻找他的方法。

    拿这幅图做例子:

    Dog 开始寻找定义的方法,向右一步,进入自己的 单例类 #Dog,然后应该做什么,选择向上么?是走 他的父类 Class,还是 应该往 单例类的继承链往上找呢?

    《元编程》文末的几句话,似乎在暗示黄色这条线的寻找方向,但是作者并没有真正说清楚:

    三、寻找答案

    我先放出答案,如下图所示:

    对象的方法,遵循

    “向右一步,然后向上查找”。

    类方法的查找是我们关心的,可以看到实际结果是,它沿着继承的单例类一路向上,然后再进入父类。

    寻找这个答案的过程中,我看了挺多资料和文字,还有问一些 Ruby 方面的朋友都没有真正分析到这一步。

    我最后是怎么找到答案的呢? 这就得借助 Ruby 自身完善的自省机制。(吐槽,其他语言可能都没有实现的那么细致)。

    其实 Ruby 自身的很多属性都绑定在自身了,直接向 Ruby 问答案就好了

     class C def a_method 'C#a_method()' end end class C class << self def a_class_method '#C.a_class_method() #singleton' end end end class D < C;end obj = D.new D.a_class_method # => '#C.a_class_method() #singleton' 

    我们知道 obj.ancestors 可以打印继承关系,但是这个很遗憾的是它不会打印 单例类。

    单例类实际上是一个隐藏的存在。这也就是研究这个问题很难得地方,因为隐藏,似乎只能通过源码和外部资料去查看。

    实际上我们是可以拿到 obj.singleton_class 的,然后我们前面分析了一些结论,大致给出了一个对象的继承模型。

    obj.singleton_class.ancestors # => [#<Class:#<D:0x00007feae092efc8>>, D, C, Object, Kernel, BasicObject] 

    就可以打印出,对象查找的顺序。

    同理,我们想要知道 D 的方法的查找顺序

    D.singleton_class.ancestors # => [#<Class:D>, #<Class:C>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] 

    这个其实就是 D 查找方法的顺序,可以看到,他先是把所有的单例类走了一遍,然后开始进入自己的父。

    四、总结

    最后,这句话

    “向右一步,然后向上查找”。

    有了新内涵, 向右一步的过程中,优先的走单例类(如果有的话)以及单例的继承,结束后,开始进入自己真正的父,即向上在继承关系中寻找。

    单例类也可以看成是一种外挂方法(比喻不严谨但是很好理解),先在外挂方法里找,也可以顺着外挂继承链找。找不到再到继承关系里面找。

    题外话

    有人可能会问,为啥继承体系要搞得那么复杂?

    借用 《元编程》里面的一句

    这样你就可以在 D 中 调用 C 的方法了。

    把对象穿成链表,然后相当于你可以拥有和复用这个链条上所有的方法。

    元编程的一部分思想也就是动态的修改、创造、转发方法。还有 《元编程》里面提到的 “自由方法”我的理解就像是把继承链中某些方法复制,然后粘贴到当前对象执行,在继承链上跳跃执行方法……

    这一切都是为了极大地自由。

    我以前一致不太理解“Ruby 是快乐优先”这句话是什么意思,现在我的理解这种快乐就是自由,拥有自由的快乐。

    其他

    图片来自文章

    安利一波作者图片配色

    参考

    书籍推荐 《 Ruby 元编程(第 2 版)》

    BLOG

    有更好的方法可以告诉我,我最新在学习 Ruby

    最新的修改会更新在 BLOG

    我的博客

    RubyChina 讨论帖

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3661 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 40ms UTC 05:10 PVG 13:10 LAX 21:10 JFK 00:10
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86