2009-05-05 15 views
61
class Hello 
@hello = "hello" 
    def display 
     puts @hello 
    end 
end 

h = Hello.new 
h.display 

Creé la clase de arriba. 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?¿Cuándo se establecen las variables de instancia de Ruby?

Respuesta

88

Las variables de instancia en Ruby pueden ser un poco confuso cuando primero aprender Ruby, especialmente si usted está acostumbrado a otro lenguaje orientado a objetos como Java .

No puede simplemente declarar una variable de instancia.

Una de las cosas más importantes que debe saber sobre las variables de instancia en ruby, aparte de la notación con un prefijo de signo @, es que se activan la primera vez que se asignan a.

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

Usted puede utilizar el método Object#instance_variables para listar 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 deben estar disponibles públicamente es usar los métodos de Módulo attr_accessor (lectura/escritura), attr_writer (escribir) y attr_reader (leer). 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 a la utilización de la Hello#hello= método sintetizado.

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

métodos Dentro auto será el objeto sobre el que se invoca el método, por lo tanto, usted está tratando de 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 para leer una variable de instancia no existente).

+2

Usted dice que "cobran vida la primera vez que se asignan", sin embargo, el OP mostró un ejemplo con asignación (aparente) antes que en su ejemplo, y el problema experimentado es que dicha variable no había cobrado vida. – kaleidic

+1

@kaleidic Exactamente. Estoy un poco desconcertado por el número de votaciones ascendentes en esta respuesta que no aborda la pregunta del OP. De hecho, la variable de instancia de clase '@ hello' * does * se activa en la fila 2 del código de ejemplo, pero el problema es que esa no es la variable a la que se refiere la fila 4. Ver mi respuesta a continuación para más detalles. –

+0

Lo siento, de hecho respondes la pregunta al final, la cual me perdí en la primera lectura. –

42

es necesario agregar un método initialize:

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

h = Hello.new 
h.display 
21

La primera @hello en el código se llama una variable de instancia de clase.

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

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

Puede obtener todo esto y mucho más aclarado al ver this collection of $5-each screencasts from The Pragmatic Programmers.

(O puede pedir aclaraciones aquí y voy a tratar de actualizar.)

+0

Un buen [artículo] (http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/) elaborando variables de instancia de nivel de clase. –

9

hay una descripción clara en el libro "El lenguaje de programación ruby", léalo será muy útil. Pego aquí (desde el capítulo 7.1.16):

una variable de instancia se utiliza dentro de una definición de clase, pero fuera de una definición del método instancia es una instancia de la clase variable de.

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 

......

Debido a las variables de instancia de clase son sólo variables de instancia de la 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 descriptores de acceso definidos, podemos hacer referencia a nuestros datos en bruto como Point.n, Point.totalX y Point.totalY.

1

También recomiendo mirar las variables de clase, que llevan el prefijo "@@" - aquí algunos ejemplos de código para mostrar cómo de clase e instancia VARs 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 
4

me había olvidado que hay era un concepto de "variable de instancia de clase" en Ruby. En cualquier caso, el problema del PO parecía desconcertante, y en realidad no se abordó en ninguna de las respuestas hasta ahora, excepto por una pista en la respuesta de Kch: es un problema de alcance. (Añadido el de edición: En realidad, la respuesta de los IRS hace dirección de este punto al final, pero voy a dejar que esta respuesta se destacan de todos modos, ya que creo que el código de ejemplo podría ser útil para la comprensión del problema.)

En una clase ruby, un nombre de variable comenzando con @ puede referirse a uno de dos variables: ya sea a una instancia variable de o a una instancia clase variable de, dependiendo de donde en la clase se hace referencia. Este es un gotcha bastante sutil.

Un ejemplo aclarará el punto. He aquí una pequeña clase de Ruby prueba (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 

me nombran las variables de acuerdo a lo que yo pensaba que eran, sin embargo, que resulta no ser siempre el caso:

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 y @instance_variable siempre se comportan como era de esperar: el primero se define en el nivel de clase, y ya sea que se haga 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 emite los valores de @@class_variable y los dos @class_instance_variable s como se esperaba, es decir, como se inicializa en la parte superior de la clase. Sin embargo, en los métodos de instancia se accede initialize y instance_method, diferentes variables de del mismo nombre, es decir, variables de instancia, no variables de instancia de clase.

se puede ver que la asignación en el método initialize no afectó a la instancia de clase variable de @class_instance_variable_1, porque la llamada después de class_method da salida a su valor anterior, "WTF". En su lugar, el método initializedeclaró una nueva variable de instancia, que es también con el nombre (engañosamente) @class_instance_variable_1. El valor asignado a él, "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 declaró y se inicializa como una variable de instancia de clase, pero cuando un método de instancia se refiere a una variable de ese nombre, que en realidad ve una instancia variable con el mismo nombre - uno que nunca fue declarado, por lo que su valor es nulo.

+0

solo esta respuesta tiene sentido :) – InQusitive

+0

@InQusitive ¡Me alegro de ser de ayuda! –

Cuestiones relacionadas