Ruby class variable另一奇怪现象的可能的解释
2013年4月24日 18:00
Ruby的class variable由于其奇怪的特性,一直是Ruby中包含争议的部分,大部分Rubist表示不使用class variable,最早我看到的文章是这篇写于2007年的帖子,里面的
@@avar = 1 class A @@avar = "hello" end puts @@avar # => hello
成为了一个经典案例,至于其解释就是Ruby的class variable不属于owner类本身,而属于它的继承结构。@@avar实际定义在main所在类Object中,A继承自Object,因此也继承了@@avar这个class variable。
而昨天我又发现了另一个奇怪的特性
class A @@a = 1 def f @@a end def f=(v) @@a = v end end A.new.f #=> 1 A.new.f = 2 A.new.f #=> 2 class A def self.f1 @@a end class << self def f2 @@a end end end A.f1 #=> 2 A.f2 #=> 2 # 直到这里,所有内容都可以理解 class << A def f3 @@a end end A.f3 #=> warning: class variable access from toplevel #=> NameError: uninitialized class variable @@a in Object
这段代码的问题在于
class << A; ... ;end
和
class A; class << self; ... ; end; end
这两段代码本该是没有区别的,但是用后者访问的到A的class variable @@a而用前者却访问不到
当晚我把这段代码在Ruby Tuesday上提出过,也没人能回答我。回去以后仔细想想,可能觉得class variable对于gateway scope的敏感程度和其它两种变量类型local variable和instance variable不一样,在Ruby中,local variable不能穿越gateway scope存在,比如
a = 1 class A b = 2 def f local_variables end end A.new.f #=> []
无论是a还是b,都没能进入到A的instance method f的定义中。而instance variable虽然也不能穿越gateway scope,但是等到gateway scope相同的时候却可以恢复出来
class A def self.f1 @a = 1 end def f2 @a = 2 end def self.f3 @a end def f4 @a end end A.f1 A.f3 #=> 1 a = A.new a.f2 a.f4 #=> 2
但是class variable和它们恐怕都不一致,它是可以穿越gateway scope而存在的,比如
class A @@a = 1 def f @@a end def f=(v) @@a = v end def self.f1 @@a end end A.new.f #=> 1 A.new.f = 2 A.new.f #=> 2 A.f
可以看到,@@a在class A定义的类定义内就像全部变量一样的存在,轻松突破gateway scope,可以出现在类定义所在的每个角落。如果是这样的话,那就可以猜测了,class variable表现和其他变量类型不一致,对它而言只有class Xxxx的语句才是真正的gateway scope,其它语句统统ignore掉,包括class << Xxxxx在内。可以用以下代码证明:
class << A @@a = 1 end @@a #=> 1 self.class #=> Object self.class.class_variables #=> [:@@a]
看上去class variable定义在了A类中,但其实定义在了main所在类Object中。