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


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

h = Hello.new
h.display

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

6 63

6 ответов:

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

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

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

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 в объекте, который не существует (обратите внимание, что совершенно законно читать несуществующую переменную экземпляра).

вам нужно добавить initialize способ:

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

h = Hello.new
h.display

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

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

технически, когда вы находитесь в пределах class область, ваш self устанавливается на объект текущего класса, и @variables относятся к текущему self. Парень, я не умею объяснять такие вещи.

вы можете сделать все это и многое другое разъяснил вам, наблюдая эта коллекция из $ 5-каждый скринкасты от прагматичных программистов.

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

есть четкое описание в книге "Язык программирования Ruby", прочитать это будет очень полезно. Я вставляю его сюда (из главы 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_reader, и attr_accessor для создания методы доступа для них.

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

С помощью этих методов доступа, мы можем ссылаться на наши необработанные данные как точки.Н, точка.totalX, и точка.полностью.

я забыл, что в Ruby существует понятие" переменная экземпляра класса". В любом случае, проблема OP казалась озадачивающей и не была действительно рассмотрена ни в одном из ответов до сих пор, за исключением намека в ответе kch: это проблема масштаба. (Добавлено на редактирование: на самом деле, ответ 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

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

метод класса с воображаемым именем class_method выводит значения @@class_variable и два @class_instance_variables как и ожидалось, то есть, как инициализируется в верхней части класса. Однако в экземпляре методы 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 в исходной задаче: он объявлен и инициализирован как переменная экземпляра класса, но когда метод экземпляра ссылается на переменную с этим именем, он на самом деле видит переменная экземпляра с тем же именем -- тот, который никогда не был объявлен, поэтому его значение является нулевым.

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

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