- PVSM.RU - https://www.pvsm.ru -
В последнее время было много публикаций на эту тему, но у новичков снова и снова возникают вопросы. Публикую свой вариант обьяснения почему не работает как ожидается переменная экземпляра, объявленная в теле класса:
class User
@var = 1
end
Переменные экземпляра работают так, как от них ожидают в обычных ситуациях:
class A
def set_a( v ); @a = v; end
def get_a; return @a; end
end
o1 = A.new
o1.instance_variables # => []
o1.get_a # => nil
o1.set_a 123
o1.instance_variables # => ["@a"]
o1.get_a # => 123
o2 = A.new
o2.get_a # => nil
Переменная появляется в экземпляре после ее установки, дальше все работает как по маслу — она видна только в своем экземпляре.
Неожиданности с переменныой экземпляра обычно начинаются когда нужно установить значение ее по-умолчанию:
class B
attr_accessor :b # вместо set_b, get_b из вышестоящего примера
@b = 1
end
o1 = B.new
o1.b # => nil
o1.instance_variables # => []
o1.b = 2
o1.b # => 2
o1.instance_variables # => ["@b"]
Оп-па. А где же наше значение по-умолчанию? Почему переменной вообще нет в обьекте с самого начала?
Такие переменные называются Class-Level Instance Variable (как это по-русски «переменные экземпляра Class-уровня»?)
На Ruby, что любой код выполняется в контексте текущего обьекта (экземпляра класса), текущий объект всегда доступен из псевдо-переменной self. Это базовые истинины. Но попорбуйте ответить на вопрос, в каком конексте (объекте) выполняется строка 4 и строка 2?
1: class User
2: @b = 1
3: def a
4: @a = 1
5: end
6: end
Строка 4 выполнится в экземпляре класса User, как мы и ожидаем, в объекте который вы создадите через User.new
А строка 2 выполняется в экземпляре класса Class, в объекте который описывает класс User. Этот объект создатся сразу после чтения интерпретатором класса User.
Само слово User это не что иное как консанта, значение которой объект типа Class [1]. Этот объект описывает наш класс User. Именно к этому объекту относится переменная @b.
o1 = B.new
o1.b # => nil
o1.instance_variables # => []
B.instance_variables # => ["@b"]
На самом деле она всегда была там. Не в классе B и не в экземпляре класса B, а в экземпляре класса Class хранимом в константе B.
Что нам дают переменные экземпляра Class-уровня?
Они могут работать как личные переменные класса. В отличии от настоящих переменных класса, которые доступных во всех потомках и экземплярах под одним именем, наша переменная принадлежит только тому классу в контексте которого была объявлена.
class B
@b = 1
def self.set_b( v ); @b = v; end
def self.get_b; @b; end
end
B.get_b # => 1
B.set_b 2
B.get_b # => 2
class B1 < B; end
B1.get_b # => 1
B1.set_b 3
B1.get_b # => 3
B.get_b # => 2
То есть при каждом наследовании от класса, в котором была объявлена такая переменная будет создаваться новая независимая. Сравните с поведением переменной класса:
class C
@@c = 1
def self.set_c( v ); @@c = v; end
def self.get_c; @@c; end
end
C.get_c # => 1
C.set_b 2
C.get_b # => 2
class C1 < C; end
C1.get_c # => 2
C1.set_c 3
C1.get_c # => 3
C.get_c # => 3
class C2 < C; @@c = 4; end
C.get_c # => 4
C1.get_c # => 4
C2.get_c # => 4
Такие переменные с помощью акцессоров создаются следующим образом:
class A
class << self
attr_accessor :a
end
# Вместо этого такая строка: self.class.attr_accessor :a
# невозможна только потому, что attr_accessor это private method :(
end
A.instance_variables # => ["@a"]
A.class_variables # => []
A.a # => nil
A.a = 123
A.a # => 123
Обратите внимание что рельсовый cattr_accessor :a создаст методы с такими же именами, только обращаться они будут к переменной класса.
class User
cattr_accessor :a
end
User.instance_variables # => []
User.class_variables # => ["@@a"]
railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/ [2]
wiseheartdesign.com/articles/2006/09/22/class-level-instance-variables/ [3]
snippets.dzone.com/posts/show/4382 [4]
Уважаемые читатели, если не трудно, накидайте сюда еще ссылок по теме, чтобы можно было использовать этот материал как учебный по теме.
Автор: dapi
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/20655
Ссылки в тексте:
[1] консанта, значение которой объект типа Class: http://habrahabr.ru/blogs/ruby/111738/
[2] railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
[3] wiseheartdesign.com/articles/2006/09/22/class-level-instance-variables/: http://wiseheartdesign.com/articles/2006/09/22/class-level-instance-variables/
[4] snippets.dzone.com/posts/show/4382: http://snippets.dzone.com/posts/show/4382
[5] Источник: http://habrahabr.ru/post/111781/
Нажмите здесь для печати.