Когда устанавливаются переменные экземпляра Ruby?

class Hello
@hello = "hello"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

Я создал класс выше. Он ничего не печатает. Я думал, что переменная экземпляра @hello была установлена ​​во время объявления класса. Но когда я вызываю метод отображения, выход "nil". Каков правильный способ сделать это?

Ответ 1

Переменные экземпляра в ruby ​​могут быть немного запутанными при первом изучении Ruby, особенно если вы привыкли к другому языку OO, например Java.

Вы не можете просто объявить переменную экземпляра.

Одна из самых важных вещей, которые нужно знать о переменных экземпляра в ruby, помимо обозначения с префиксом @sign, заключается в том, что они spring в жизни при первом назначении.

class Hello
  def create_some_state
    @hello = "hello"
  end
end

h = Hello.new
p h.instance_variables 

h.create_some_state
p h.instance_variables

# Output
[]
["@hello"]

Вы можете использовать метод Object#instance_variables для отображения всех переменных экземпляра объекта.

Обычно вы объявляете и инициализируете все переменные экземпляра в методе initialize. Другой способ четко документировать, какие переменные экземпляра должны быть общедоступными, - это использовать методы модуля attr_accessor (чтение/запись), attr_writer (запись) и attr_reader (чтение). Эти методы будут синтезировать различные методы доступа для указанной переменной экземпляра экземпляра.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

Переменная экземпляра все еще не создана до тех пор, пока не будет назначено использование синтезированного метода Hello#hello=.

Еще одна важная проблема, как описано в kch, заключается в том, что вам нужно знать о различных контекстах, активных при объявлении класса. При объявлении класса приемник (self) по умолчанию в самой внешней области будет объектом, который представляет сам класс. Следовательно, ваш код сначала создаст переменную экземпляра класса при назначении @hello на уровне класса.

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

Ответ 2

Вам нужно добавить метод initialize:

class Hello
    def initialize
        @hello = "hello"
    end
    def display
        puts @hello
    end
end

h = Hello.new
h.display

Ответ 3

Первый @hello в вашем коде называется переменной экземпляра класса.

Это переменная экземпляра объекта класса, на которую указывает константа Hello. (и который является экземпляром класса Class.)

Технически, когда вы находитесь в области Class, ваш self установлен в объект вашего текущего класса, а @variables относится к вашему текущему self. Мальчик, я сосать, объясняя это.

Вы можете получить все это и многое другое уточнить, просмотрев эту коллекцию из 5-ти каждый скринкастов от The Pragmatic Programmers.

(Или вы можете запросить разъяснения здесь, и я попробую обновить.)

Ответ 4

есть четкое описание в книге "Язык рубинового программирования", прочитайте, это будет очень полезно. Я вставляю его здесь (из главы 7.1.16):

Переменная экземпляра, используемая внутри определения класса, но вне определение метода экземпляра - это переменная экземпляра класса.

class Point
    # Initialize our class instance variables in the class definition itself
    @n = 0              # How many points have been created
    @totalX = 0         # The sum of all X coordinates
    @totalY = 0         # The sum of all Y coordinates

    def initialize(x,y) # Initialize method 
      @x,@y = x, y      # Sets initial values for instance variables
    end

    def self.new(x,y)   # Class method to create new Point objects
      # Use the class instance variables in this class method to collect data
      @n += 1           # Keep track of how many Points have been created
      @totalX += x      # Add these coordinates to the totals
      @totalY += y

      super             # Invoke the real definition of new to create a Point
                    # More about super later in the chapter
    end

    # A class method to report the data we collected
    def self.report
        # Here we use the class instance variables in a class method
        puts "Number of points created: #@n"
        puts "Average X coordinate: #{@totalX.to_f/@n}"
        puts "Average Y coordinate: #{@totalY.to_f/@n}"
    end
end

......

Поскольку переменные экземпляра класса являются просто переменными экземпляра класса объектов, мы можем использовать attr, attr_reader и attr_accessor для создания методы доступа для них.

class << self
  attr_accessor :n, :totalX, :totalY
end

Если эти аксессоры определены, мы можем ссылаться на наши исходные данные как Point.n, Point.totalX и Point.totalY.

Ответ 5

Я забыл, что в Ruby существует концепция класса экземпляра класса. В любом случае проблема ОП казалась загадочной и на самом деле не затрагивалась ни в одном из ответов до сих пор, за исключением подсказки в kch: это проблема сферы. (Добавлено в edit: На самом деле, ответ sris действительно подходит к этой точке в конце, но я все равно оставлю этот ответ, так как я думаю, что примерный код может быть полезен для понимания проблемы.)

В классе Ruby имя переменной, начинающееся с @, может относиться к одной из двух переменных: либо к переменной экземпляра, либо к переменной экземпляра класса в зависимости от того, в каком классе он ссылался. Это довольно тонкая добыча.

Пример пояснит суть. Здесь немного тестового класса Ruby (весь код проверен в irb):

class T

  @@class_variable = "BBQ"
  @class_instance_variable_1 = "WTF"
  @class_instance_variable_2 = "LOL"

  def self.class_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def initialize
    @instance_variable = "omg"
    # The following line does not assign a value to the class instance variable,
    # but actually declares an instance variable withthe same name!
    @class_instance_variable_1 = "wtf"
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

  def instance_method
    puts "@@class_variable           == #{@@class_variable           || 'nil'}"
    # The following two lines do not refer to the class instance variables,
    # but to the instance variables with the same names.
    puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
    puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
    puts "@instance_variable         == #{@instance_variable         || 'nil'}"
  end

end

Я назвал переменные в соответствии с тем, что, как я думал, они были, хотя это не всегда так:

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

irb> t = T.new
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">

irb> t.instance_method
@@class_variable           == BBQ
@class_instance_variable_1 == wtf    # the value of the instance variable
@class_instance_variable_2 == nil    # the value of the instance variable
@instance_variable         == omg
=> nil

irb> T.class_method
@@class_variable           == BBQ
@class_instance_variable_1 == WTF    # the value of the class instance variable
@class_instance_variable_2 == LOL    # the value of the class instance variable
@instance_variable         == nil    # does not exist in the class scope
=> nil

@@class_variable и @instance_variable всегда ведут себя так, как вы ожидали: первый определяется на уровне класса и относится ли это к методу класса или методу экземпляра, он присваивает значение, присвоенное ему Вверх. Последний получает значение только в объекте класса T, поэтому в методе класса он ссылается на неизвестную переменную, значение которой nil.

Метод класса, образно обозначенный class_method, выводит значения @@class_variable и два @class_instance_variable, как ожидалось, то есть, как инициализировано в верхней части класса. Однако в примерах методов initialize и instance_method доступны разные переменные с одним и тем же именем, то есть переменные экземпляра, а не переменные экземпляра класса.

Вы можете видеть, что назначение в методе initialize не повлияло на переменную экземпляра класса @class_instance_variable_1, потому что более поздний вызов class_method выводит свое старое значение, "WTF". Вместо этого метод initialize объявил новую переменную экземпляра, которая также называется (ошибочно) @class_instance_variable_1. Назначенное ему значение "WTF" выводится с помощью методов initialize и instance_method.

Переменная @class_instance_variable_2 в примере кода эквивалентна переменной @hello в исходной задаче: она объявлена ​​и инициализируется как переменная экземпляра класса, но когда метод экземпляра ссылается на переменную этого имени, он фактически видит переменная экземпляра с тем же именем, которая никогда не была объявлена, поэтому ее значение равно nil.

Ответ 6

Я бы также рекомендовал посмотреть переменные класса с префиксом "@@" - вот пример кода, чтобы показать вам, как разные классы и экземпляры:

class Vars
  @@classvar="foo"
  def test
    @instancevar="bar"
  end
  def Vars.show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"
  end
  def instance_show
    puts "classvar: #{@@classvar}"
    puts "instancevar: #{@instancevar}"

  end
end

# only shows classvar since we don't have an instance created
Vars::show
# create a class instance
vars = Vars.new
# instancevar still doesn't show b/c it hasn't been initialized
vars.instance_show
# initialize instancevar
vars.test
# now instancevar shows up as we expect
vars.instance_show