@@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 &lt; Vehicle 那么它也将有类变量@@number_of_wheels

【讨论】:

这意味着如果您创建该类的子类,它将继承该变量。因此,如果您有一个带有类变量 @@number_of_wheels 的类 Vehicle,那么如果您创建一个 class Car &lt; Vehicle,那么它也将具有类变量 @@number_of_wheels 如果我有一个class Vehicle@number_of_wheels,那么class Car &lt; 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 &lt;&lt; self end 块的作用,特别是 对于其他对class &lt;&lt; self 感到困惑的人,请参阅this

以上是关于@@variable 在 Ruby 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

& 是啥意思。 (& 点)在 Ruby 中是啥意思?

||= 在 Ruby 中是啥意思? [复制]

这个语法在ruby中是啥意思[重复]

||= 在 Ruby 语言中是啥意思? [复制]

<< 在 Ruby 中是啥意思?

Groovy在英文中是啥意思