Когда устанавливаются переменные экземпляра Ruby? - PullRequest
64 голосов
/ 06 мая 2009
class Hello
@hello = "hello"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

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

Ответы [ 6 ]

91 голосов
/ 06 мая 2009

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

42 голосов
/ 06 мая 2009

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

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

h = Hello.new
h.display
22 голосов
/ 06 мая 2009

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

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

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

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

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

10 голосов
/ 11 ноября 2010

в книге «Язык программирования 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, attr_reader и attr_accessor для создания методы доступа к ним.

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

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

5 голосов
/ 25 декабря 2014

Я забыл, что в Ruby существует концепция «переменная экземпляра класса». В любом случае, проблема ОП казалась загадочной, и до сих пор ее не решали ни в одном из ответов, за исключением подсказки в ответе КЧ: это проблема охвата. (Добавлено при редактировании: на самом деле, ответ 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.

1 голос
/ 20 февраля 2011

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...