之所以会出现这种相当混乱的行为,是因为 Ruby 解释器将局部变量定义置于比方法调用更高的优先级。这里有一致性,但除非你事先知道它是如何工作的,否则很难看清楚。
鉴于 Ruby 中的很多东西都是对象和方法调用,因此很自然地假设变量定义是某种在某物(如内核或 main 或定义它的对象或其他任何东西)上调用的某种方法,并且结果变量是某种对象。如果是这样的话,你会猜测解释器会根据方法查找的规则解决变量定义和其他方法之间的名称冲突,并且如果它没有找到同名的方法,只会定义一个新变量首先是潜在的变量定义。
但是,变量定义不是方法调用,变量也不是对象。相反,变量只是对对象的引用,变量定义是解释器在语言表面下跟踪的东西。这就是为什么Kernel.local_variables
返回一个符号数组,而没有办法获得某种局部变量对象的数组。
因此,Ruby 需要一组特殊的规则来处理变量和方法之间的名称冲突。非局部变量有一个特殊的前缀来表示它们的作用域($、@ 等),它可以解决这个问题,但对于局部变量则不然。如果 Ruby 在方法之后需要括号,那也可以解决这个问题,但我们可以不必这样做。为了方便地引用不带前缀的局部变量和调用不带括号的方法,该语言默认假设您需要局部变量,只要它在范围内。它本来可以设计成另一种方式,但是你会遇到奇怪的情况,你定义了一个局部变量,并且它在程序中途被一些具有相同名称的遥远方法立即黯然失色,所以这样可能更好。
Ruby 编程语言,p。88,有这个说法:
"...局部变量没有标点符号作为前缀。这意味着局部变量引用看起来就像方法调用表达式。如果 Ruby 解释器看到对局部变量的赋值,它知道它是一个变量并且NameError
“ _
它继续解释为什么你nil
在打电话address
时得到make_root_cert
:
“因此,一般来说,在初始化之前尝试使用局部变量会导致错误。有一个怪癖——当 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"
您使用的 setter 方法会attr_accessor
导致解释器在调用 setter 方法之前创建一个变量,但必须调用它才能为该变量分配除nil
. address = "something"
in在被调用的方法中get_defaults
定义了一个局部变量,该变量在方法address
结束时超出范围。当你调用make_root_cert
时,没有局部变量调用address
,所以address
你得到的getter方法attr_accessor
被调用并返回nil
,因为setter方法没有被调用给它一些其他值。self.address=
让解释器知道您需要类方法address=
而不是新的局部变量,从而解决歧义。