Когда устанавливаются переменные экземпляра 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