metodo - que es una instancia en ruby
¿Cuándo se establecen las variables de instancia de Ruby? (6)
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?
El primer @hello
en su 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 alcance de la class
, tu self
está configurado para el objeto de tu clase actual, y las @variables
pertenecen a tu self
actual. Chico, apestaba por explicar estas cosas.
Puede obtener todo esto y mucho más aclarado al ver esta colección de $ 5, cada grabación de pantalla de The Pragmatic Programmers .
(O puede solicitar aclaraciones aquí e intentaré actualizar).
Había olvidado que había 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. (Agregado en edición: en realidad, la respuesta de sris aborda este punto al final, pero dejaré esta respuesta de todos modos, ya que creo que el código de ejemplo podría ser útil para entender el problema).
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 , dependiendo de a qué parte de la clase se haga referencia. 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
Las nombré de acuerdo con lo que pensé que eran, aunque eso no siempre es 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, conserva el valor asignado a él en la parte superior. Este último solo obtiene un valor en un objeto de 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 denominado imaginativamente class_method
emite los valores de @@class_variable
y las dos @class_instance_variable
s como se esperaba, es decir, como se inicializó 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 de 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 cambio, el método initialize
declaró una nueva variable de instancia, que también se denomina (engañosamente) @class_instance_variable_1
. El valor asignado a él, "wtf"
, es generado por 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 e inicializa como una variable de instancia de clase, pero cuando un método de instancia hace referencia a una variable de ese nombre, en realidad ve una variable de instancia con el mismo nombre , uno que nunca fue declarado, por lo que su valor es nulo.
Las variables de instancia en ruby pueden ser un poco confusas al aprender Ruby, especialmente si está acostumbrado a otro lenguaje OO 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 @ sign, es que se activan la primera vez que se asignan .
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 usar el método Object#instance_variables
para listar todas las variables de instancia de un objeto.
Normalmente "declaras" e inicializas todas las variables de instancia en el método de inicialización. Otra forma de documentar claramente las variables de instancia que deberían estar disponibles al público es utilizar los métodos del Módulo attr_accessor
(lectura / escritura), attr_writer
(escritura) y attr_reader
(lectura). 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 usar el método sintetizado Hello#hello=
.
Otro tema importante, como se describe en kch, es que debes tener en cuenta 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í misma. Por lo tanto, su código creará primero una variable de instancia de clase al asignar a @hello
en el nivel de clase.
Los métodos internos self serán el objeto sobre el que se invoca el método, por lo tanto, estás tratando de imprimir el valor de una variable de instancia con el nombre @hello
en el objeto, que no existe (ten en cuenta que es perfectamente legal leer un non variable de instancia existente).
Necesita agregar un método de initialize
:
class Hello
def initialize
@hello = "hello"
end
def display
puts @hello
end
end
h = Hello.new
h.display
También recomendaría buscar variables de clase que tengan el prefijo "@@" - aquí hay un código de muestra para mostrarle cómo la clase y los vars de 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
hay una descripción clara en el libro "El lenguaje de programación Ruby", léalo que 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 definición de 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
......
Como las variables de instancia de clase son solo variables de instancia de objetos de clase, 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 accesos definidos, podemos referirnos a nuestros datos brutos como Point.n, Point.totalX y Point.totalY.