【问题标题】:Extend module & class variable access?扩展模块和类变量访问?
【发布时间】:2013-04-12 12:55:22
【问题描述】:

我不明白为什么在下面的例子中访问模块的类变量会失败:

module M
  @@xyz = 123
end
M.class_variables    # [:@@xyz]
M.class_variable_get :@@xyz    # 123 , so far so good

class C
  extend M
end
C.singleton_class.class_variables    # [:@@xyz]
C.singleton_class.class_variable_get :@@xyz # NameError:
                      # uninitialized class variable @@xyz in Class

谁能解释为什么 @@xyz 的类变量在 C 的单例类中突然无法访问/未定义?

更新: 我用不同的 Ruby YARV 版本重新测试了上面的代码,发现它是最新的回归。

更新 2:

在最新一代的 Ruby 中,Module#class_variables 方法的定义发生了变化。

  • Ruby 到 1.9.3 的定义是

    class_variables → 数组

    返回 mod 中类变量名称的数组。

  • Ruby 2.0.0 最新稳定版

    class_variables(inherit=true) → 数组

    返回 mod 中类变量名称的数组。这 在任何包含的模块中包含类变量的名称, 除非inherit参数设置为false。

所以在最新的 Ruby 版本中,class_variables 默认返回包含模块的类变量。只是好奇这个功能有什么用,或者它是否仍然与 include 而不是 extend “包含”的模块有关。

谁能解释一下?

【问题讨论】:

  • 我从C.singleton_class.class_variables 得到了[]。你是怎么得到结果的?
  • 你使用什么 Ruby 实现?在我的问题中,我提到它已使用 Ruby 1.9.3 和 2.0.0 进行了测试。 Sawa 莫名其妙地抹掉了这张纸条。 真丢脸,sawa ;)
  • 我正在使用C:\>ruby -v ruby 1.9.3p374 (2013-01-15) [i386-mingw32]
  • @RubyLovely 这很奇怪。我使用系统安装中的 Ruby 2.0.0p0 和 rbenv(用户安装)中的 Ruby 1.9.3p392。 Linux x86_64。

标签: ruby


【解决方案1】:

关于特定的代码示例,它在我的 Ruby 1.9.3p194 上的工作方式不同:

module M
  @@xyz = 123
end
M.class_variables    # [:@@xyz]
M.class_variable_get :@@xyz    # 123 , so far so good

class C
  extend M
end
C.singleton_class.class_variables    # [] - HERE BE THE DIFFERENCE
C.singleton_class.class_variable_get :@@xyz # NameError:

当然,这就是我所期望的。 C.singleton_class 是 Class Class 的直接子类,我在任何地方都没有看到您在 Class 或其后代上设置任何类变量...但我相信您的代码按照您在机器上编写的方式工作。

【讨论】:

  • 所以它看起来像一个回归和问题报告给错误跟踪器。
  • 我将在之前的 Ruby 版本上进行测试并报告结果。
  • 差异是由于最新2.0.0版本的变化。 class_variables 还从包含的模块返回类变量 inherited。还是很好奇@@xyz怎么可以同时被继承和不可访问。
【解决方案2】:

这不是答案,只是问题的一些问题。

如果我们在C 类中包含模块MC 会获取在M 中定义的类变量:

module M
  @@xyz = 123
end

class C
  include M
end

C.class_variables   #=> [:@@xyz]
C.class_variable_get(:@@xyz)  # 123

在类定义中调用extend M 等同于在该类的特征类(或单例类)中调用include M

module M
  @@xyz = 123
end

eigenclass = class C
  class << self
    include M
    self
  end
end

eigenclass.class_variables            #=>[:@@xyz]
eigenclass.class_variable_get(:@@xyz) #=>NameError: uninitialized class variable @@xyz in Class

似乎区别在于 Ruby 对待普通类和特征类的方式不同。

【讨论】:

    【解决方案3】:

    简而言之,您观察到的差异是因为模块的工作方式与类不同。不久前我向我的上级提出了这个问题:Inheriting class methods from mixins

    我得出结论,虽然 Module 在某些方面类似于 Class,但在其他方面,Ruby 将其简单地视为普通对象。特别是,在其他对象(单例方法等)中,被称为“类东西”的类(类方法、类变量......)只是被称为“单例东西”。模块的单例类在很多方面都有像模块一样被视为普通对象。

    【讨论】:

    • 我明白了,但是为什么在单例类上调用的 class_variables 确实返回“混合”类变量 @@xyz ?这是一个需要报告的错误候选者吗?
    • 实际上,我必须承认我提供了一个笼统的答案。您的代码在我的 Ruby 1.9.3 上甚至不能以同样的方式工作。例如,C.singleton_class.class_variables 行为我返回空数组 []...
    【解决方案4】:

    不确定这些是否是答案,但我确实找到了这些

    C::M.class_variables #=> ["@@xyz"]
    # (but gives "warning: toplevel constant M referenced by C::M")
    

    class D
      include M
    end
    D.class_variables #=> ["@@xyz"]
    

    (来自 Ruby 1.8.7,目前手头没有更高版本)。

    include 使模块的实例方法成为类的实例方法。根据 Pickaxe 的说法,“就好像模块成为使用它的类的超类一样”。

    同时,extend 打算将模块的方法添加到 对象;在类定义中调用时,它相当于self.extend。看来他们是不等价的。

    HTH。

    【讨论】:

    • 我明白includeextend 之间的区别。简而言之,问题是为什么class_variable_get 找不到变量@@xyz,即使class_variables 确实返回了它的符号!
    • @DavidUnric C.singleton_class.class_variables 应该给[]
    • @RubyLovely 它可能应该,因为根据extend 的文档,它应该只添加实例方法。然而,当前基于 Ruby YARV 的实现 1.9.x 和 2.0.0 确实返回带有模块扩展类的单例类的 [:@@xyz]
    猜你喜欢
    • 2011-05-03
    • 1970-01-01
    • 2020-12-14
    • 1970-01-01
    • 2016-05-14
    • 2014-02-09
    • 2017-06-18
    • 2013-05-11
    • 2016-09-13
    相关资源
    最近更新 更多