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

StackOverflow https://stackoverflow.com/questions/826734

  •  05-07-2019
  •  | 
  •  

Вопрос

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

h = Hello.new
h.display

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

Это было полезно?

Решение

Переменные экземпляра в 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 на уровне класса.

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

Другие советы

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

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

h = Hello.new
h.display

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

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

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

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

Я совсем забыл, что в 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

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

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

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top