Ruby中方法&常量的查找路径

来源:互联网 发布:java怎么使用log4j 编辑:程序博客网 时间:2024/06/03 18:46

Ruby中方法继承、方法查找路径、constant查找路径
@(Ruby)


    • 继承类方法和实例方法
    • 方法调用路径
    • 常量查找路径
    • 总结

继承类方法和实例方法

class A  def method1  end  def self.method2  endendclass B < A  def method3  end  def self.method4  endendb = B.newb.method1 #nilb.method3 #nilB.method2 #nil

可以看到,B不仅继承了A的实例方法,还继承了A的 singleton方法。

方法调用路径

module Include  def call(level)    puts "#{level} include"    super(level + 1) rescue nil  endendmodule Prepend  def call(level)    puts "#{level} prepend"    super(level + 1) rescue nil  endendmodule Extend  def call(level)    puts "#{level} extend"    super(level + 1) rescue nil  endendclass Super  def call(level)    puts "#{level} super"    super(level + 1) rescue nil  endendclass Klass < Super  include Include  prepend Prepend  def call(level)    puts "#{level} klass"    super(level + 1) rescue nil  endendthing = Klass.newdef thing.call(level)  puts "#{level} singleton"  super(level + 1) rescue nilendthing.extend(Extend)thing.call(1)

输出结果为:

1 singleton2 extend3 prepend4 klass5 include6 super

此时继承链为

>  Klass.ancestors=> [Prepend, Klass, Include, Super, Object, Kernel, BasicObject]>  thing.singleton_class.ancestors=> [#<Class:#<Klass:0x007fe42a89d5a8>>, Extend, Prepend, Klass, Include, Super, Object, Kernel, BasicObject]

可以看到实例 thing 上的方法的寻找路径是根据它的单例类的继承链来决定的。

常量查找路径

常量的查找是按照下面的规则进行的:
- 首先查找Module.nesting路径,这个Module.nesting跟方法的调用没关系,而是根据方法定义的位置而确定的,
- 否则查找Module.nesting.first.ancestors
- 如果Module.nesting.firstnil或者module,查找Object.ancestors

1、通过Module.nesting查找,路径为[A].

class A  def get_c    C  endendclass B < A  module C; endendB.new.get_c=> "NameError: uninitialized constant A::C"

可以看到,虽然B中定义了常量C,但是get_c没有从B中开始查找,而是从自己被定义的模块A开始查找,找不到而报错。

2、Module.nesting找不到,则从Module.nesting.first.ancestors开始查找,此时为会去A的父类AParent中查找。

class AParent  C = 'AParent'endclass A < AParent  def get_c    C  endendclass B < A  C = 'in B'endB.new.get_c=> "AParent"

3、 如果Module.nesting.firstnil或者module类型,则从Object查找:

class AParent  C = 'AParent'endclass A < AParent  module M    def get_c      C    end  endendclass B  extend A::MendB.get_c=> "NameError: uninitialized constant A::M::C"

因为Object中没有C常量,因此会报错,我们为Object加上常量C,此时运行结果如下:

class Object  C = 'In Object'endclass AParent  C = 'AParent'endclass A < AParent  module M    def get_c      C    end  endendclass B  extend A::MendB.get_c=> "In Object"

总结

这是一段Ruby内建的常量查找代码,使用了 binding.eval ,不管在任何地方调用,都能
保证查找的起点都是从它被定义的地方。

class Binding  def const(name)    eval <<-CODE, FILE, LINE + 1      modules = Module.nesting + (Module.nesting.first || Object).ancestors      modules += Object.ancestors if Module.nesting.first.class == Module      found = nil      modules.detect do |mod|        found = mod.const_get(#{name.inspect}, false) rescue nil      end      found or const_missing(#{name.inspect})    CODE  endend

参考文章:
https://cirw.in/blog/constant-lookup.html
http://pascalbetz.github.io/ruby/2016/03/14/lookup-path/

0 0
原创粉丝点击