【问题标题】:Why do I get an undefined local variable or method error when using a constant, but not when using a method?为什么在使用常量时会出现未定义的局部变量或方法错误,而在使用方法时却不会?
【发布时间】:2016-12-28 18:41:06
【问题描述】:

如果我有这样的课程怎么办:

class Thing
  def number
    10
  end
end

而我是这样继承的:

class OtherThing < Thing
  CONSTANT = number / 2
end

当我尝试实例化类时,我得到undefined local variable or method 'number',但如果我这样做:

   class OtherThing < Thing
     def method_instead_of_constant
       number / 2
     end
   end

有效吗?

编辑

我不一定要寻找一种技巧来完成这项工作,而是要了解它为什么不能。 mudasobwa下面的回答最有帮助;常量是在类级别分配的,而不是在实例上。

【问题讨论】:

  • 阅读 Ruby 中的 self... 这对您有很大帮助。

标签: ruby inheritance constants


【解决方案1】:

你需要一个类方法来实现你正在寻找的功能:

class Thing
  #   ⇓⇓⇓⇓   HERE
  def self.number
    10
  end
end

class OtherThing < Thing
  CONSTANT = number / 2
end

CONSTANT 赋值发生在类级别,因此它可以访问Thing 的类方法,但不能访问它的实例方法。

另一方面,您可以实例化Thing,然后在其上调用实例方法:

class Thing
  def number
    10
  end
end

class OtherThing < Thing
  #          ⇓⇓⇓⇓⇓⇓⇓⇓⇓  HERE
  CONSTANT = Thing.new.number / 2
end

【讨论】:

  • 我认为最终目标不是以这种方式定义该常量。这只是“这行得通,那行不通”的一个例证
  • 或许是。谁知道:)
  • @SergioTulentsev 是的,我刚刚丢了两个例子,展示了“什么有效,什么无效”:)
  • 啊,我明白了,我没有意识到常量是在类级别创建的,我以为它们在每个实例上。有趣的谢谢!
【解决方案2】:

因为numberThing 上的实例方法。 OtherThing 的类定义范围是Class 的实例,这意味着它在定义时不是Thing 的实例或OtherThing 的实例。

也就是说,您不应该通过执行方法来定义常量。您可能有一个计算类变量,您可以调用 freeze 以防止在启动后进行编辑,但即使这样也不是很常见的模式。

【讨论】:

    【解决方案3】:

    因为范围。

    方法调用基于动态范围进行评估,并区分类和实例范围。常量在词法范围内解析,并且不像方法那样区分类和实例范围。

    class A
      # expressions are evaluated in scope of class A
      def m
        # expressions are evaluated in scope of an instance of A
        return 42
      end
    end
    

    类和实例不一样。

    A 
    a = A.new
    
    A.class # => Class
    a.class # => A
    
    A.respond_to?(:m) # => false
    a.respond_to?(:m) # => true
    
    A.m # => raises NoMethodError
    a.m # => 42
    

    因此在类主体中不能调用实例方法。

    然而,常量是使用词法范围查找的,即源文件中类和模块的周围上下文。

    module M
      # can access global constants and those defined in M
      class A
        # can access global constants and those defined in M or A
        def m
          # can access global constants and those defined in M or A
        end
      end
    end
    

    您可以使用Module.nesting检查当前常量查找路径

    【讨论】:

      【解决方案4】:

      我不一定要寻找破解方法来完成这项工作,但是 [原文如此] 了解为什么不这样做。

      这是错误信息:

      undefined local variable or method 'number'
      

      当 Ruby 解释器看到 number 时,它会寻找一个局部变量或一个名为 number 的方法。在方法的上下文中,解释器将number 读取为self.number。所以这一行:

      CONSTANT = number / 2
      

      实际上被认为是:

      CONSTANT = self.number / 2
      

      那么self 是什么?

      这取决于您定义它的位置(显式或隐式)。在类块中,self 是类本身,即OtherThing。由于OtherThing 及其任何祖先都没有定义类方法number,也不存在变量number,Ruby 会抛出错误消息。

      在实例方法定义中定义的self 是当前对象。但没有例子和更多的理论,这是非常抽象的。其他相关主题是单例类继承。如果您喜欢书籍,那么我推荐David A. Black 的The Well-Grounded Rubyist 2nd Ed, Chapter 5。三思而后行,阅读/研究整本书。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-16
        • 1970-01-01
        • 1970-01-01
        • 2012-10-14
        相关资源
        最近更新 更多