ruby - 在 Ruby 中,在 class << self 中定义的方法中,为什么父类上定义的常量不能在没有 self 的情况下访问?
问题描述
我试图更好地理解 Ruby 单例和类继承。我到处读到
def self.method_name; end`
相当于
class << self
def method_name; end
end
但如果这是真的,那么我会期望print_constant_fails
工作,但事实并非如此。这里发生了什么?
class SuperExample
A_CONSTANT = "super example constant"
end
class SubExample < SuperExample
def self.print_constant_works_1
puts A_CONSTANT
end
class << self
def print_constant_works_2
puts self::A_CONSTANT
end
def print_constant_fails
puts A_CONSTANT
end
end
end
pry(main)> SubExample.print_constant_works_1
super example constant
pry(main)> SubExample.print_constant_works_2
super example constant
pry(main)> SubExample.print_constant_fails
NameError: uninitialized constant #<Class:SubExample>::A_CONSTANT
from (pry):13:in `print_constant_fails'
解决方案
您遇到了一个常见的 Ruby 问题 - 常量查找。
常量查找中最重要的概念是Module.nesting
(与方法查找不同,主要起点是self
)。此方法为您提供当前模块嵌套,Ruby 解释器在解析常量标记时直接使用该嵌套。修改嵌套的唯一方法是使用关键字class
,module
并且它只包括您使用该关键字的模块和类:
class A
Module.nesting #=> [A]
class B
Module.nesting #=> [A::B, A]
end
end
class A::B
Module.nesting #=> [A::B] sic! no A
end
在元编程中,可以使用Class.new
or动态定义模块或类Module.new
- 这不会影响嵌套,并且是一个非常常见的错误原因(啊,还值得一提 - 在 Module.nesting 的第一个模块上定义常量):
module A
B = Class.new do
VALUE = 1
end
C = Class.new do
VALUE = 2
end
end
A::B::VALUE #=> uninitialized constant A::B::VALUE
A::VALUE #=> 2
上面的代码将产生两个警告:一个是常量 A::VALUE 的双重初始化,另一个是重新分配常量。
如果它看起来像“我永远不会那样做” - 这也适用于其中定义的所有常量RSpec.describe
(内部调用 Class.new),所以如果你在 rspec 测试中定义一个常量,它们肯定是全局的(除非你明确说明要在其中定义的模块self::
)
现在让我们回到您的代码:
class SubExample < SuperExample
puts Module.nesting.inspect #=> [SubExample]
class << self
puts Module.nesting.inspect #=> [#<Class:SubExample>, SubExample]
end
end
解析常量时,解释器首先遍历其中的所有模块并在该模块中Module.nesting
搜索该常量。因此,如果嵌套是[A::B, A]
并且我们正在寻找带有 token 的常量C
,解释器将首先寻找A::B::C
然后寻找A::C
。
但是,在您的示例中,这两种情况都会失败:)。然后解释器开始搜索 Module.nesting 中第一个(也是唯一一个)模块的祖先。SubrExample.singleton_class.ancestors
给你:
[
#<Class:SubExample>,
#<Class:SuperExample>,
#<Class:Object>,
#<Class:BasicObject>,
Class,
Module,
Object,
Kernel,
BasicObject
]
如您所见 - 没有SuperExample
模块,只有它的单例类 - 这就是为什么在内部进行常量查找class << self
失败(print_constant_fails
)。
的祖先Subclass
是:
[
SubExample,
SuperExample,
Object,
Kernel,
BasicObject
]
我们在SuperExample
那里,所以解释器将设法SuperExample::A_CONSTANT
在这个嵌套中找到。
我们只剩下print_constant_works_2
. 这是一个单例类的实例方法,所以self
在这个方法中只是SubExample
. 因此,我们正在寻找SubExample::A_CONSTANT
- 不断查找首先搜索SubExample
它的所有祖先,包括SuperExample
.
推荐阅读
- javascript - 成功登录时更改窗口位置
- ios - 如何防止 navigationBarTitle 占满全角?
- php - 是否有一种编程方式来区分 bash 脚本是由用户执行还是通过 PHP 中的 shell_exec 执行?
- python - 在 mypy 中为什么 boolean 会传递浮点类型注释
- java - 如何在 java 中使用终端转义码?
- python - 在 python subprocess.popen 中通过标准输入发送输入
- ui5-tooling - 新项目的ui5初始化失败
- androidviewclient - ValueError:收到的不包含有效的 XML:已终止
- python - 为什么这里的多重赋值会产生意想不到的结果?
- python - Rabbitmq/Pika - 在 python 中获取给定队列的消费者列表