una que metodo instancia initialize ruby syntax instance-variables

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.