为什么Ruby类变量的行为如此奇怪?
我只是花了很多时间在为什么Ruby类变量的行为如此奇怪?,ruby,Ruby,我只是花了很多时间在IRB(实际上,PRY)上玩,试图弄清楚类变量在Ruby中是如何工作的,我完全被我的发现所迷惑 从我所看到的(如果我错了,请纠正我),类变量在类、这些类的实例、子类和子类的实例之间共享。但是,对于子类及其实例,如果类变量已分配给超类,则类变量仅共享给超类。如果它在超类中没有被赋值,那么它在超类中保持未定义状态,直到它被赋值到该变量成为共享的点为止。。。搞什么鬼?(如果您感到困惑,请参见下面的示例。) 为什么会这样?我在某处听说Ruby类变量基于Smalltalk中类似的概念,
IRB
(实际上,PRY
)上玩,试图弄清楚类变量在Ruby中是如何工作的,我完全被我的发现所迷惑
从我所看到的(如果我错了,请纠正我),类变量在类、这些类的实例、子类和子类的实例之间共享。但是,对于子类及其实例,如果类变量已分配给超类,则类变量仅共享给超类。如果它在超类中没有被赋值,那么它在超类中保持未定义状态,直到它被赋值到该变量成为共享的点为止。。。搞什么鬼?(如果您感到困惑,请参见下面的示例。)
为什么会这样?我在某处听说Ruby类变量基于Smalltalk中类似的概念,但我真的不明白为什么需要这种行为
示例: 在
superfoo\u和\u subbar.rb中
:
class SuperFoo
def class_var_x=(x)
@@x = x
end
def class_var_x
@@x
end
end
class SubBar < SuperFoo
# Define these again, just in case they're bound at compile time or something...
def class_var_x=(x)
@@x = x
end
def class_var_x
@@x
end
end
新的PRY会议:
# Now let's try this again:
require './superfoo_and_subbar' # => true
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
SubBar.new.class_var_x # NameError: uninitialized class variable @@x in SubBar
# So far so good. Let's set x on SubBar first this time
SubBar.new.class_var_x = 2
SubBar.new.class_var_x # => 2
# Okay, so x is now set on SubBar so it should also be set on SuperFoo then, right?
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
# Wait, what? So they're seperate variables now?
SubBar.new.class_var_x # => 2
SuperFoo.new.class_var_x # NameError: uninitialized class variable @@x in SuperFoo
# It certainly looks that way. What happens if I set x on SuperFoo then?
SuperFoo.new.class_var_x = 3
SuperFoo.new.class_var_x # => 3
SubBar.new.class_var_x # => 3
# Wait, so now they're the same varaible again? What the heck?
SubBar.new.class_var_x = 4
SuperFoo.new.class_var_x # => 4
SubBar.new.class_var_x # => 4
# ...WHY!?!? Seriously, what's the point of this?
这就是继承的工作原理。是类变量被共享给它的子类,一旦超级类将定义它
SubBar.new.class\u var\u x
仅在classSubBar
中创建class变量。所以很明显,它的超级类将无法访问它
class Foo
def x_class_var=(val)
@@x =val
end
def x_class_var
@@x
end
end
class Bar<Foo
def x_class_var=(val)
@@x =val
end
def x_class_var
@@x
end
end
Foo.class_variables # => []
Bar.class_variables # => []
Bar.new.x_class_var = 10
Foo.class_variables # => []
Bar.class_variables # => [:@@x]
现在
@@x
是在Foo
类中创建的,因此作为Foo
的子类,Bar
可以访问@@x
。这是因为类变量可以从超类到子类共享,而不是从子类到超类共享,正如我在上面的示例中所示。在第一次PRY会话中,当您选中SubBar.new.class\u var\u x
时,它会检查@@class\u var\u x
是否存在。由于它确实存在于SuperFoo
和SubBar
中,因此需要SuperFoo
的@@class\u var\u x
在第二个会话中,当您选中
SuperFoo.new.class\u var\u x
时,它没有查看SubBar
@@class\u var\u x,因为父类的对象不关心从它们继承的类,因此它创建了一个新的@@class\u var\u x
实例,但随后我分配给SuperFoo.new.class\u var\u x
,突然SubBar.new.class\u var\u x
引用了SuperFoo
中的值,而不是我以前分配给它的值?谢谢,这是一个关于类变量如何工作的非常好的心理模型。实际上,对于这个问题,我更关心的是“为什么”,而不是“如何”。(例如,Ruby为什么会这样做?有没有人会希望以这种方式使用类变量的原因?)+1:它的行为与此类似,因为SubBar中的方法被覆盖。如果它像往常一样被子类化:SubBar
,它的行为将像您预期的那样。@steenslag我不会说“像我预期的那样”,因为老实说,我的第一直觉是类变量只是类上实例变量的语法糖。您是对的,尽管不重写这两个方法会使我的第二个示例的行为更像第一个。@Arup我刚刚观察到,当从代码中删除被重写的方法时,不会出现令人惊讶的行为。(大卫一个黑色的例子一点也不让我惊讶!令人担忧……)
class Foo
def x_class_var=(val)
@@x =val
end
def x_class_var
@@x
end
end
class Bar<Foo
def x_class_var=(val)
@@x =val
end
def x_class_var
@@x
end
end
Foo.class_variables # => []
Bar.class_variables # => []
Bar.new.x_class_var = 10
Foo.class_variables # => []
Bar.class_variables # => [:@@x]
Foo.class_variables # => []
Bar.class_variables # => []
Foo.new.x_class_var = 10
Foo.class_variables # => [:@@x]
Bar.class_variables # => [:@@x]