Когда устанавливаются переменные экземпляра Ruby?
class Hello
@hello = "hello"
def display
puts @hello
end
end
h = Hello.new
h.display
Я создал класс выше. Он ничего не печатает. Я думал, что переменная экземпляра @hello была установлена во время объявления класса. Но когда я вызываю метод отображения, вывод "nil". Как правильно это сделать?
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_variable
s как и ожидалось, то есть, как инициализируется в верхней части класса. Однако в экземпляре методы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