Kiedy są ustawiane zmienne instancji Ruby?
class Hello
@hello = "hello"
def display
puts @hello
end
end
h = Hello.new
h.display
Stworzyłem klasę powyżej. Nic nie drukuje. Myślałem, że zmienna instancji @ hello została ustawiona podczas deklaracji klasy. Ale kiedy wywołuję metodę display, wyjście jest 'nil'. Jak to zrobić?
6 answers
Zmienne instancji w Rubim mogą być nieco mylące podczas pierwszej nauki Rubiego, zwłaszcza jeśli jesteś przyzwyczajony do innego języka oo, takiego jak Java.
Nie można po prostu zadeklarować zmiennej instancji.
Jedną z najważniejszych rzeczy, które należy wiedzieć o zmiennych instancji w Rubim, poza zapisem z prefiksem znaku@, jest to, że pojawiają się one przy pierwszym przypisaniu do.
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"]
Możesz użyć metody Object#instance_variables
, aby wyświetlić listę wszystkich instancji zmienne obiektu.
Zwykle "deklarujesz" i inicjalizujesz wszystkie zmienne instancji w metodzie initialize. Innym sposobem na wyraźne udokumentowanie, które zmienne instancji powinny być publicznie dostępne, jest użycie metod modułu attr_accessor
(read/write), attr_writer
(write) I attr_reader
(read). Metody te będą syntetyzować różne metody dostępu dla wymienionej zmiennej instancji.
class Hello
attr_accessor :hello
end
h = Hello.new
p h.instance_variables
h.hello = "hello"
p h.instance_variables
# Output
[]
["@hello"]
Zmienna instancji nadal nie jest tworzona, dopóki nie zostanie przypisana do Hello#hello=
metoda.
Inną ważną kwestią, jak opisano w kch, jest to, że musisz być świadomy różnych kontekstów aktywnych podczas deklarowania klasy. Podczas deklarowania klasy, domyślny odbiornik (self) w zewnętrznym zakresie będzie obiektem reprezentującym samą klasę. Dlatego Twój kod najpierw utworzy zmienną instancji klasy przy przypisywaniu do @hello
na poziomie klasy.
Wewnątrz metod self {[25] } będzie obiektem, na którym metoda jest wywoływana, dlatego próbujesz wydrukować wartość zmiennej instancji o nazwie @hello
w obiekcie, który nie istnieje(zauważ, że odczyt nieistniejącej zmiennej instancji jest całkowicie legalny).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-31 13:19:26
Musisz dodać metodę initialize
:
class Hello
def initialize
@hello = "hello"
end
def display
puts @hello
end
end
h = Hello.new
h.display
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-05 20:22:24
Pierwsza @hello
w Twoim kodzie nazywa się zmienną instancji klasy.
Jest zmienną instancyjną obiektu klasy, na którą wskazuje stała Hello
. (i która jest instancją klasy Class
.)
Technicznie, gdy jesteś w zakresie class
, twój {[4] } jest ustawiony na obiekt Twojej bieżącej klasy, a @variables
odnosi się do twojego bieżącego self
. Nie umiem tego wytłumaczyć.
Możesz to wszystko i wiele więcej wyjaśnić, oglądając to zbiór $5-każdy screencasty z programu Pragmatic .
(lub możesz poprosić o wyjaśnienia tutaj, a ja postaram się zaktualizować.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-05 20:47:18
Jest jasny opis w książce "język programowania ruby", przeczytanie go będzie bardzo pomocne. Wklejam go tutaj (z rozdziału 7.1.16):
Zmienna instancji używana wewnątrz definicji klasy, ale poza definicja metody instancji jest zmienną instancji klasy .
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
......
Ponieważ zmienne instancji klasy są tylko zmiennymi instancji klasy obiektów, możemy użyć attr, attr_reader i attr_accessor do utworzenia metody dostępu do nich.
class << self
attr_accessor :n, :totalX, :totalY
end
Z tymi accesorami zdefiniowanymi, możemy odnosić się do naszych nieprzetworzonych danych jako punkt.n, Punkt.totalX i punkt.totalnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-27 04:45:25
Zapomniałem, że w Rubim istnieje koncepcja "zmiennej instancji klasy". W każdym razie, problem OP wydawał się zastanawiający i tak naprawdę nie został rozwiązany w żadnej z odpowiedzi do tej pory, z wyjątkiem podpowiedzi w odpowiedzi kch: to problem zakresu. (Dodano przy edycji: właściwie, odpowiedź sris odnosi się do tego punktu na końcu, ale i tak odpowiem, ponieważ myślę, że przykładowy kod może być przydatny do zrozumienia problemu.)
W klasie Ruby, zmienna nazwa zaczynająca się od @
może odnosić się do jednej z dwóch zmiennych: albo do zmiennej instancji lub do zmiennej instancji klasy , w zależności od tego, gdzie w klasie jest ona odwołana. To dość subtelne.
Przykład wyjaśni sprawę. Oto mała klasa testowa Ruby (cały kod testowany w 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
Nazwałem zmienne zgodnie z tym, co myślałem, że są, choć okazuje się, że nie zawsze jest to case:
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
i @instance_variable
zawsze zachowują się tak, jak można się spodziewać: pierwsza jest zdefiniowana na poziomie klasy i niezależnie od tego, czy jest ona określona w metodzie klasowej, czy w metodzie instancji, posiada przypisaną jej wartość na górze. Ta ostatnia otrzymuje wartość tylko w obiekcie klasy T
, więc w metodzie klasy odnosi się do nieznanej zmiennej, której wartością jest nil
.
Metoda klasy o nazwie class_method
wyświetla wartości @@class_variable
i dwóch @class_instance_variable
s zgodnie z oczekiwaniami, czyli jako / align = "left" / Jednak w przypadku metod initialize
iinstance_method
, dostępne są różne zmienne o tej samej nazwie, to znaczy zmienne instancji , a nie zmienne instancji klasy.
Widać, że przypisanie w metodzie initialize
nie miało wpływu na zmienną instancji klasy @class_instance_variable_1
, ponieważ późniejsze wywołanie class_method
wyświetla jej starą wartość, "WTF"
. Zamiast tego metoda initialize
zadeklarował nową zmienną instancji, , która jest również nazwany ([13]}. Przypisana do niego wartość, "wtf"
, jest wyprowadzana za pomocą metod initialize
i instance_method
.
Zmienna @class_instance_variable_2
w kodzie przykładu jest równoważna zmiennej @hello
w pierwotnym problemie: jest zadeklarowana i zainicjalizowana jako zmienna instancji klasy, ale gdy metoda instancji odnosi się do zmiennej o tej samej nazwie, widzi zmienną instancji o tej samej nazwie -- taką, która nigdy nie została zadeklarowana, więc jej wartość jest zerowa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-17 14:16:05
Polecam również spojrzeć na zmienne klasy, które są poprzedzone znakiem "@ @ " - oto przykładowy kod, który pokaże, jak różne są zmienne klasowe i instancyjne:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-20 15:56:15