【问题标题】:Ruby module variable accessor not working as expectedRuby 模块变量访问器未按预期工作
【发布时间】:2012-12-19 22:32:41
【问题描述】:

所以我想要一个带有变量和访问方法的模块。
我的代码看起来像这样

module Certificates
  module Defaults

  class << self
    attr_accessor :address

    def get_defaults
      address = "something"
      make_root_cert
    end

    def make_root_cert
      blub = address
      # do somthing
    end
  end
end

我用撬来检查它。
结果是

  • Certificates::Defaults 具有称为 address 和 address= 的方法。
  • 如果我在 get_defaults 方法中调用地址,它会按预期返回“某物”
  • 如果我在 make_root_cert 中调用它,它会返回 nil

我在另一个模块中使用了这种创建 attr_accessor 的方式,并且效果很好。我希望我只是误解了 ruby​​ 的工作方式,有人可以解释为什么这个例子不起作用。也许使用 ruby​​ 对象模型的实现细节。

杰里米是对的。

我的发现

这对我来说似乎不一致。

  • 如果您使用表达式“地址”并且未设置实例变量,则返回局部变量
  • 如果已设置实例变量而未设置局部变量,则返回实例变量。
  • 如果两者都已设置,则返回局部变量。

另一方面,address="test" 总是设置局部变量。

【问题讨论】:

    标签: ruby


    【解决方案1】:

    在您的 get_defaults 方法中,address 是一个局部变量。要使用 setter,您必须输入以下内容:

    self.address = "something"
    

    这将正确调用address= 方法。

    【讨论】:

      【解决方案2】:

      出现这种相当混乱的行为是因为 Ruby 解释器将局部变量定义置于比方法调用更高的优先级。这里有一致性,但除非你事先知道它是如何工作的,否则很难看清楚。

      鉴于 Ruby 中的很多东西都是对象和方法调用,很自然地假设变量定义是某种方法调用的东西(如内核或 main 或定义它的对象或其他)并且结果变量是某种对象。如果是这样的话,你会猜测解释器会根据方法查找的规则解决变量定义和其他方法之间的名称冲突,并且如果它没有找到同名的方法,只会定义一个新变量首先是潜在的变量定义。

      但是,变量定义不是方法调用,变量也不是对象。相反,变量只是对对象的引用,变量定义是解释器在语言表面下跟踪的东西。这就是为什么Kernel.local_variables 返回一个符号数组,而没有办法获得某种局部变量对象的数组。

      因此,Ruby 需要一组特殊的规则来处理变量和方法之间的名称冲突。非局部变量有一个特殊的前缀来表示它们的作用域($、@ 等),它可以解决这个问题,但对于局部变量则不然。如果 Ruby 在方法之后需要括号,那也可以解决这个问题,但我们可以不必这样做。为了方便地引用不带前缀的局部变量和调用不带括号的方法,该语言默认假设您需要局部变量,只要它在范围内。它本来可以设计成另一种方式,但是你会遇到奇怪的情况,你定义了一个局部变量,并且它在程序中途被一些遥远的同名方法立即黯然失色,所以这样可能更好。

      The Ruby Programming Language, p. 88,有话要说:

      "...局部变量没有标点符号作为前缀。这意味着局部变量引用看起来就像方法调用表达式。如果 Ruby 解释器 看过一个局部变量的赋值,它知道它是一个变量而不是一个方法,它可以返回变量的值。如果没有赋值,那么 Ruby 将表达式视为方法调用。如果不存在该名称的方法,Ruby 将引发 NameError。"

      它继续解释为什么你在make_root_cert 中调用address 时得到nil

      “因此,一般来说,在初始化之前尝试使用局部变量会导致错误。有一个怪癖——当 Ruby 解释器看到该变量的赋值表达式时,变量就会存在。这是即使没有实际执行该赋值,也会出现这种情况。存在但尚未赋值的变量将被赋予默认值nil。例如:

         a = 0.0 if false # This assignment is never executed
         print a          # Prints nil: the variable exists but is not assigned
         print b          # NameError: no variable or method named b exists"
      

      使用attr_accessor 获得的setter 方法会导致解释器在调用setter 方法之前创建一个变量,但必须调用它才能为该变量分配nil 以外的值。 address = "something" in get_defaults 在该方法中定义了一个名为 address 的局部变量,该变量在方法结束时超出范围。当您调用make_root_cert 时,没有称为address 的局部变量,因此您使用attr_accessor 获得的getter 方法address 被调用并返回nil,因为尚未调用setter 方法给它一些其他值。 self.address= 让解释器知道您需要类方法 address= 而不是新的局部变量,从而解决歧义。

      【讨论】:

        猜你喜欢
        • 2014-03-28
        • 2021-07-18
        • 2020-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-19
        • 2018-01-17
        • 1970-01-01
        相关资源
        最近更新 更多