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 initialize
declaró 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.
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
@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. –
Lo siento, de hecho respondes la pregunta al final, la cual me perdí en la primera lectura. –