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ć?

Author: pez_dispenser, 2009-05-06

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).

 90
Author: sris,
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
 42
Author: Nathan Kitchen,
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ć.)

 22
Author: kch,
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.

 9
Author: lfx_cool,
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_variables 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.

 4
Author: Teemu Leisti,
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
 1
Author: Steve Midgley,
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