@@variable 在 Ruby 中是啥意思?
Posted
技术标签:
【中文标题】@@variable 在 Ruby 中是啥意思?【英文标题】:What does @@variable mean in Ruby?@@variable 在 Ruby 中是什么意思? 【发布时间】:2011-08-18 21:46:50 【问题描述】:什么是前面带有双 at 符号 (@@
) 的 Ruby 变量?我对以 at 符号开头的变量的理解是它是一个实例变量,就像 php 中这样:
PHP版本
class Person
public $name;
public function setName($name)
$this->name = $name;
public function getName()
return $this->name;
Ruby 等效
class Person
def set_name(name)
@name = name
end
def get_name()
@name
end
end
@@
的双 at 符号是什么意思,它与单 at 符号有何不同?
【问题讨论】:
我不知道,但我感觉它在盯着我看。我现在有点害怕用 Ruby 编写代码...... TL;公众博士:100 次中有 99 次,我会使用“类实例”变量(self
方法中的@
)而不是类变量(@@
)。请参阅下面的答案中的一连串原因。
【参考方案1】:
答案是部分正确的,因为@@ 实际上是一个类变量,它是每个类层次结构的,这意味着它由一个类、它的实例及其后代类及其实例共享。
class Person
@@people = []
def initialize
@@people << self
end
def self.people
@@people
end
end
class Student < Person
end
class Graduate < Student
end
Person.new
Student.new
puts Graduate.people
这将输出
#<Person:0x007fa70fa24870>
#<Student:0x007fa70fa24848>
所以对于 Person、Student 和 Graduate 类只有一个相同的 @@variable,并且这些类的所有类和实例方法都引用同一个变量。
还有另一种定义在类对象上定义的类变量的方法(请记住,每个类实际上是某个东西的实例,它实际上是 Class 类,但这是另一回事)。您使用@ 表示法而不是@@,但您不能从实例方法访问这些变量。您需要有类方法包装器。
class Person
def initialize
self.class.add_person self
end
def self.people
@people
end
def self.add_person instance
@people ||= []
@people << instance
end
end
class Student < Person
end
class Graduate < Student
end
Person.new
Person.new
Student.new
Student.new
Graduate.new
Graduate.new
puts Student.people.join(",")
puts Person.people.join(",")
puts Graduate.people.join(",")
这里,@people 每个类而不是类层次结构是单个的,因为它实际上是存储在每个类实例上的变量。这是输出:
#<Student:0x007f8e9d2267e8>,#<Student:0x007f8e9d21ff38>
#<Person:0x007f8e9d226158>,#<Person:0x007f8e9d226608>
#<Graduate:0x007f8e9d21fec0>,#<Graduate:0x007f8e9d21fdf8>
一个重要的区别是,您不能直接从实例方法访问这些类变量(或您可以说的类实例变量),因为实例方法中的@people 将引用 Person 或 Student 的特定实例的实例变量或研究生课程。
因此,虽然其他答案正确地指出 @myvariable(带有单个 @ 表示法)始终是一个实例变量,但这并不一定意味着它不是该类的所有实例的单个共享变量。
【讨论】:
不太正确。 @myvariable 绝不是“该类所有实例的单个共享变量”。它是一个对象的实例变量,其范围仅适用于该对象。如果该对象没有为该变量提供访问器,那么其他对象,即使它们是声明该变量的类的实例,也将无权访问它。这种情况实际上与访问属于任何其他对象的实例变量的能力没有什么不同。【参考方案2】:当一个类扩展或包含该模块时,模块中的@ 和 @@ 的工作方式也不同。
给定
module A
@a = 'module'
@@a = 'module'
def get1
@a
end
def get2
@@a
end
def set1(a)
@a = a
end
def set2(a)
@@a = a
end
def self.set1(a)
@a = a
end
def self.set2(a)
@@a = a
end
end
然后你会得到下面显示为 cmets 的输出
class X
extend A
puts get1.inspect # nil
puts get2.inspect # "module"
@a = 'class'
@@a = 'class'
puts get1.inspect # "class"
puts get2.inspect # "module"
set1('set')
set2('set')
puts get1.inspect # "set"
puts get2.inspect # "set"
A.set1('sset')
A.set2('sset')
puts get1.inspect # "set"
puts get2.inspect # "sset"
end
class Y
include A
def doit
puts get1.inspect # nil
puts get2.inspect # "module"
@a = 'class'
@@a = 'class'
puts get1.inspect # "class"
puts get2.inspect # "class"
set1('set')
set2('set')
puts get1.inspect # "set"
puts get2.inspect # "set"
A.set1('sset')
A.set2('sset')
puts get1.inspect # "set"
puts get2.inspect # "sset"
end
end
Y.new.doit
因此,在模块中使用@@ 表示您希望对其所有用途通用的变量,并在模块中使用@ 表示您希望在每个使用上下文中分开的变量。
【讨论】:
【参考方案3】:@
- 类的实例变量@@
- 类变量,在某些情况下也称为静态变量
类变量是在类的所有实例之间共享的变量。这意味着从此类实例化的所有对象仅存在一个变量值。如果一个对象实例更改了变量的值,则该新值对于所有其他对象实例都会发生本质上的变化。
考虑类变量的另一种方式是在单个类的上下文中作为全局变量。
类变量通过在变量名前加上两个 @
字符 (@@
) 来声明。类变量必须在创建时初始化
【讨论】:
【参考方案4】:@@
表示类变量,即可以继承。
这意味着如果您创建该类的子类,它将继承该变量。所以如果你有一个类Vehicle
和类变量@@number_of_wheels
那么如果你创建一个class Car < Vehicle
那么它也将有类变量@@number_of_wheels
【讨论】:
这意味着如果您创建该类的子类,它将继承该变量。因此,如果您有一个带有类变量@@number_of_wheels
的类 Vehicle
,那么如果您创建一个 class Car < Vehicle
,那么它也将具有类变量 @@number_of_wheels
如果我有一个class Vehicle
和@number_of_wheels
,那么class Car < Vehicle
也会有一个名为@number_of_wheels
的实例变量。与类变量的主要区别在于类具有相同的变量,例如改变一个改变另一个。【参考方案5】:
以@
为前缀的变量是实例变量,而以@@
为前缀的变量是类变量。查看以下示例;它的输出在 puts
行末尾的 cmets 中:
class Test
@@shared = 1
def value
@@shared
end
def value=(value)
@@shared = value
end
end
class AnotherTest < Test; end
t = Test.new
puts "t.value is #t.value" # 1
t.value = 2
puts "t.value is #t.value" # 2
x = Test.new
puts "x.value is #x.value" # 2
a = AnotherTest.new
puts "a.value is #a.value" # 2
a.value = 3
puts "a.value is #a.value" # 3
puts "t.value is #t.value" # 3
puts "x.value is #x.value" # 3
可以看到@@shared
是类之间共享的;在 one 的实例中设置值会更改该类的所有其他实例甚至子类的值,其中名为 @shared
的变量和一个 @
不会是。
[更新]
正如 Phrogz 在 cmets 中提到的,在 Ruby 中使用实例变量在类本身上跟踪类级数据是一种常见的习惯用法。这可能是一个棘手的主题,并且有很多关于该主题的additional reading,但可以将其视为修改Class
类,但只是 @ 的实例987654333@ 您正在使用的课程。一个例子:
class Polygon
class << self
attr_accessor :sides
end
end
class Triangle < Polygon
@sides = 3
end
class Rectangle < Polygon
@sides = 4
end
class Square < Rectangle
end
class Hexagon < Polygon
@sides = 6
end
puts "Triangle.sides: #Triangle.sides.inspect" # 3
puts "Rectangle.sides: #Rectangle.sides.inspect" # 4
puts "Square.sides: #Square.sides.inspect" # nil
puts "Hexagon.sides: #Hexagon.sides.inspect" # 6
我包含了Square
示例(输出nil
)来证明这可能不会像您预期的那样100% 运行; article I linked above 有大量关于该主题的附加信息。
另外请记住,根据 dmarkow 的评论,与大多数数据一样,您应该非常小心 multithreaded environment 中的类变量。
【讨论】:
恕我直言,如果您包含的代码显示了如何在类级别使用实例变量来跟踪类级别数据,而不会出现在子类之间共享数据的“奇怪”行为,那么这个答案将是完美的恕我直言。跨度> 我还要指出,类变量在多线程环境(例如 Rails)中可能很危险/不可靠 嗯...在某种程度上它听起来像 PHP 中的静态变量,但继承部分不同。我不认为 PHP 有这样的东西。 我不明白ruby class << self end
块的作用,特别是
对于其他对class << self
感到困惑的人,请参阅this以上是关于@@variable 在 Ruby 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章