Что означает @@variable в Ruby?

какие переменные Ruby предшествуют знакам double at (@@)? Мое понимание переменной, предшествующей знаку at, заключается в том, что это переменная экземпляра, например, в PHP:

PHP версии

class Person {

    public $name;

    public function setName($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

Руби эквивалентно

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

что делает двойной знак @@ mean, и чем он отличается от одного знака at?

5 ответов


переменная с префиксом @ это переменная, в то время как одна начинаются с @@ - это переменной класс. Проверьте следующий пример; его вывод находится в комментариях в конце puts строки:

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 совместно используется между классами; установка значения в экземпляре одного изменяет значение для всех других экземпляров этого класса и даже дочерних классов, где переменная с именем @shared, с одним @ не будет.

[обновление]

как упоминает Phrogz в комментариях, это обычная идиома в Ruby для отслеживания данных уровня класса с переменной экземпляра о самом классе. Это может быть сложным предметом, чтобы обернуть свой ум вокруг, и есть много дополнительная информация по этому вопросу, но подумайте об этом как об изменении Class класс, но только экземпляр Class класс вы работаете вместе. Пример:

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%, как вы ожидаете; статья, которую я связал выше есть много дополнительной информации по этому вопросу.

также имейте в виду, что, как и большинство данных, вы должны быть очень осторожны с переменными класса в многопоточной среде, согласно комментарию dmarkow.


@ - переменная экземпляра класса
@@ - переменная класса, также называемая в некоторых случаях статической переменной

переменная класса-это переменная, которая является общей для всех экземпляров класса. Это означает, что для всех объектов, созданных из этого класса, существует только одно значение переменной. Если один экземпляр объекта изменяет значение переменной, это новое значение будет существенно изменено для всех других экземпляров объекта.

другой способ мышления мышление переменных класса - это глобальные переменные в контексте одного класса. Переменные класса объявляются путем префикса имени переменной с двумя @ символы (@@). Переменные класса должны быть инициализированы во время создания


@@ обозначает переменную класса, т. е. она может быть унаследована.

это означает, что если вы создадите подкласс этого класса, он унаследует переменную. Итак, если у вас есть класс Vehicle с переменной класс @@number_of_wheels тогда, если вы создаете class Car < Vehicle тогда у него тоже будет переменная класса @@number_of_wheels


@ и @@ в модулях также работают по-разному, когда класс расширяет или включает этот модуль.

учитывая

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 

затем вы получаете результаты показаны ниже в комментариях

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

поэтому используйте @@ в модулях для переменных, которые вы хотите использовать, и используйте @ в модулях для переменных, которые вы хотите разделить для каждого контекста использования.


ответы частично верны, потому что @@ на самом деле является переменной класса, которая находится в иерархии классов, что означает, что она разделяется классом, его экземплярами и его потомками и их экземплярами.

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, и все методы класса и экземпляра этих классов относятся к одной и той же переменной.

есть другой способ определения класса переменная, которая определена на объекте класса (помните, что каждый класс на самом деле является экземпляром чего-то, что на самом деле является классом класса, но это другая история). Вы используете @ нотации вместо @@, но вы не можете получить доступ к этим переменным из методов экземпляра. Вам нужно иметь обертки метода класса.

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 или Graduate.

поэтому, хотя другие ответы правильно утверждают, что @myvariable (с одной @ нотацией) всегда является переменной экземпляра, это не обязательно означает, что это ни одной общей переменной для всех экземпляров этого класса.