Pregunta

class Hello
@hello = "hello"
    def display
        puts @hello
    end
end

h = Hello.new
h.display

He creado la clase anterior. No imprime nada. Pensé que la variable de instancia @hello se estableció durante la declaración de clase. Pero cuando llamo al método de visualización, la salida es 'nil'. ¿Cuál es la forma correcta de hacer esto?

¿Fue útil?

Solución

Las variables de instancia en ruby ??pueden ser un poco confusas cuando aprendes Ruby por primera vez, especialmente si estás acostumbrado a otro lenguaje OO como Java.

No puedes simplemente declarar una variable de instancia.

Una de las cosas más importantes que debe saber acerca de las variables de instancia en ruby, aparte de la notación con el prefijo de signo @, es que cobran vida la primera vez que se las asigna .

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"]

Puede utilizar el método Object # instance_variables para enumerar todas las variables de instancia de un objeto.

Normalmente, "declara" e inicializa todas las variables de instancia en el método de inicialización. Otra forma de documentar claramente qué variables de instancia deberían estar disponibles públicamente es usar los métodos del Módulo attr_accessor (lectura / escritura), attr_writer (escribir) y attr_reader (leído). Estos métodos sintetizarán diferentes métodos de acceso para la variable de instancia listada.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

La variable de instancia aún no se crea hasta que se asigna para usar el método sintetizado Hello # hello = .

Otro problema importante, como lo describe kch, es que debe ser consciente de los diferentes contextos activos al declarar una clase. Al declarar una clase, el receptor predeterminado (self) en el ámbito más externo será el objeto que representa la clase en sí. Por lo tanto, su código creará primero una variable de instancia de clase cuando se asigne a @hello en el nivel de clase.

Dentro de los métodos self será el objeto sobre el que se invoca el método, por lo tanto, está intentando imprimir el valor de una variable de instancia con el nombre @hello en el objeto, que no existe (tenga en cuenta que es perfectamente legal leer una variable de instancia no existente).

Otros consejos

Debe agregar un método de initialize :

class Hello
    def initialize
        @hello = "hello"
    end
    def display
        puts @hello
    end
end

h = Hello.new
h.display

El primer @hello en su código se llama una variable de instancia de clase.

Es una variable de instancia del objeto de clase a la que apunta la constante Hello . (y que es una instancia de la clase Class .)

Técnicamente, cuando estás dentro del ámbito de class , tu self se establece en el objeto de tu clase actual y @variables pertenece a tu self actual. Chico, apesto en explicar estas cosas.

Puedes aclarar todo esto y mucho más viendo esta colección de $ 5, cada una de las escenas de The Pragmatic Programmers .

(O puede solicitar aclaraciones aquí y trataré de actualizar).

hay una descripción clara en el libro " El lenguaje de programación ruby ??" ;, leerlo será muy útil. Lo pego aquí (del capítulo 7.1.16):

  

Una variable de instancia utilizada dentro de una definición de clase pero fuera de una   la definición del método de instancia es una variable de instancia de clase .

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

......

  

Porque las variables de instancia de clase son solo variables de instancia de clase   objetos, podemos usar attr, attr_reader, y attr_accessor para crear   métodos de acceso para ellos.

class << self
  attr_accessor :n, :totalX, :totalY
end

Con estos accesores definidos, podemos referirnos a nuestros datos en bruto como Point.n, Point.totalX y Point.totalY.

Olvidé que había una " variable de instancia de clase " concepto en ruby. En cualquier caso, el problema del OP parecía desconcertante, y en realidad no se abordó en ninguna de las respuestas anteriores, excepto por un indicio en la respuesta de kch: es un problema de alcance. (Agregado en la edición: en realidad, la respuesta de sris hace aborda este punto al final, pero de todos modos dejaré que esta respuesta sea válida, ya que creo que el código de ejemplo podría ser útil para comprender el problema). / p>

En una clase de Ruby, un nombre de variable que comienza con @ puede referirse a una de dos variables: ya sea a una variable de instancia o a una variable de instancia de clase , según el lugar al que se haga referencia en la clase. Este es un gotcha bastante sutil.

Un ejemplo aclarará el punto. Aquí hay una pequeña clase de prueba de Ruby (todo el código probado en 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

Nombré las variables según lo que pensé que eran, aunque eso no siempre es así:

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

El @@ class_variable y @instance_variable siempre se comportan como cabría esperar: el primero se define en el nivel de clase, y si se hace referencia en un método de clase o en un método de instancia, mantiene el valor asignado en la parte superior. Este último solo obtiene un valor en un objeto de la clase T , por lo que en un método de clase, se refiere a una variable desconocida cuyo valor es nil .

El método de clase llamado imaginativamente class_method genera los valores de @@ class_variable y los dos @class_instance_variable s como se esperaba, es decir, como inicializado en la parte superior de la clase. Sin embargo, en los métodos de instancia initialize y instance_method , se accede a diferentes variables del mismo nombre , es decir, variables de instancia, no variables de instancia de clase .

Puede ver que la asignación en el método initialize no afectó a la variable de instancia de clase @ class_instance_variable_1 , porque la última llamada de class_method genera su valor anterior, " WTF " . En su lugar, el método initialize declaró una nueva variable de instancia, una que también es también llamada (de manera engañosa) @ class_instance_variable_1 . El valor asignado, " wtf " , se genera mediante los métodos initialize y instance_method .

La variable @ class_instance_variable_2 en el código de ejemplo es equivalente a la variable @hello en el problema original: se declara y se inicializa como una variable de instancia de clase, pero cuando una instancia el método se refiere a una variable de ese nombre, en realidad ve una variable de instancia con el mismo nombre , una que nunca se declaró, por lo que su valor es nulo.

También recomendaría mirar las variables de clase que tienen el prefijo " @@ " - Aquí hay algunos ejemplos de código para mostrar cómo las vars de clase e instancia son diferentes:

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top