Ruby中const_get的混乱行为?

Ruby中const_get的混乱行为?,ruby,Ruby,根据文档mod.const_get(sym)“返回mod中命名常量的值。” 我还知道,默认情况下,const_get可以查找接收方的继承链。因此,以下工作: class A; HELLO = :hello; end class B < A; end B.const_get(:HELLO) #=> :hello 然而,这就是我感到困惑的地方——模块并不是对象的子类。那么,为什么我仍然可以使用const\u get从模块中查找“全局”常量呢?为什么下面的工作 module M; end

根据文档
mod.const_get(sym)
“返回mod中命名常量的值。”

我还知道,默认情况下,
const_get
可以查找接收方的继承链。因此,以下工作:

class A; HELLO = :hello; end
class B < A; end
B.const_get(:HELLO) #=> :hello
然而,这就是我感到困惑的地方——模块并不是
对象的子类。那么,为什么我仍然可以使用
const\u get
从模块中查找“全局”常量呢?为什么下面的工作

module M; end
M.const_get(:Array) #=> Array
如果文档正确-
const_get
只需查找接收器或其超类下定义的常量。但是在上面的代码中,
对象
不是
M
的超类,那么为什么可以查找
数组


谢谢

您的困惑是正确的。。。文档没有说明Ruby为查找
模块中的常量提供了一个特例,并且已经修改。如果在正常的层次结构中找不到常量,Ruby会根据需要从
对象
重新启动查找

常量查找本身可能有点混乱。以以下为例:

module M
  Foo = :bar
  module N
    # Accessing Foo here is fine:
    p Foo # => bar
  end
end
module M::N
  # Accessing Foo here isn't
  p Foo  # => uninitialized constant M::N::Foo
end
p M::N.const_get :Foo  # => uninitialized constant M::N::Foo
不过,在这两个地方,访问
对象
级别的常量(如
数组
)都很好(感谢上帝!)。现在的情况是Ruby维护了一个“打开的模块定义”列表。如果常数具有显式作用域,例如
LookHereOnly::Foo
,则将搜索only
LookHereOnly
及其包含的模块。如果没有指定作用域(如上例中的
Foo
),Ruby将通过打开的模块定义查找常量
Foo
M::N
,然后是
M
,最后是
对象
。最顶端打开的模块定义总是
对象

因此
M::N.const\u get:Foo
相当于在打开的类只有
M::N
Object
时访问
Foo
,就像我示例的最后一部分一样


我希望我没有弄错,因为我自己仍然对不断的查找感到困惑:-)

我想出了以下脚本来加载名称间隔的常量:

def load_constant(name)
  parts = name.split('::')
  klass = Module.const_get(parts.shift)
  klass = klass.const_get(parts.shift) until parts.empty?
  klass
end

只要我们不检查错误,您就可以:

def load_constant(name)
    name.split('::').inject(Module) do |mod_path, mod_to_find|
      mod_path.const_get(mod_to_find)
    end
end

请注意,这与
的行为不匹配
SomeModule::SomeGlobalConstant
将导致错误,而
SomeModule.const\u get(:SomeGlobalConstant)
将工作。知道它为什么会这样做吗?在什么情况下这会有用?@sepp2k:不确定它是否有用,但我试图解释我认为它背后的逻辑。这不是直观的,但M::N问题是由于词法范围。如果您执行
模块M;模块N;结束;结束
,M的内容在N的词法范围内(由于嵌套,M从N可见)。具有
模块M::N;end
,M不可见,因为包含范围是顶层。就我个人而言,我尽量避免以M::N形式定义类和模块,这也是因为如果M还不存在的话,这是一个错误。。。试试eval(“M::N”),JRuby也可以用这种方法做一些有趣的事情是的,它是邪恶的
def load_constant(name)
    name.split('::').inject(Module) do |mod_path, mod_to_find|
      mod_path.const_get(mod_to_find)
    end
end