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中。