什么时候Ruby实例变量设置?

pez*_*ser 65 ruby syntax instance-variables

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

h = Hello.new
h.display
Run Code Online (Sandbox Code Playgroud)

我创建了上面的课程.它不打印任何东西.我认为在类声明期间设置了实例变量@hello.但是当我调用display方法时输出为'nil'.这样做的正确方法是什么?

sri*_*ris 91

在第一次学习Ruby时,ruby中的实例变量可能有点令人困惑,特别是如果你习惯于另一种像Java这样的OO语言.

您不能简单地声明实例变量.

除了带有@符号前缀的符号之外,了解ruby中实例变量的最重要的事情之一就是它们在第一次被赋值时就会生动.

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"]
Run Code Online (Sandbox Code Playgroud)

您可以使用该方法Object#instance_variables列出对象的所有实例变量.

您通常在initize方法中"声明"并初始化所有实例变量.另一种清楚地记录应该公开可用的实例变量的方法是使用Module方法attr_accessor(读/写),attr_writer(写)和attr_reader(读).这些方法将为列出的实例变量合成不同的访问器方法.

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]
Run Code Online (Sandbox Code Playgroud)

在将实例变量分配给使用合成Hello#hello=方法之前,仍不会创建该实例变量.

像kch所描述的另一个重要问题是,在声明一个类时,你需要了解不同的上下文.声明类时,最外层作用域中的默认接收器(self)将是表示类本身的对象.因此@hello,在类级别上分配时,您的代码将首先创建一个类实例变量.

内部方法self将是调用该方法的对象,因此您尝试@hello使用对象中的名称打印实例变量的值,该值不存在(请注意,读取非现有实例是完全合法的)变量).

  • 你说"他们第一次被分配到他们的生活中",但是OP显示了一个比你的例子早的(明显的)分配的例子,并且遇到的问题是所述变量并没有因此而生活. (3认同)

Nat*_*hen 42

您需要添加一个initialize方法:

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

h = Hello.new
h.display
Run Code Online (Sandbox Code Playgroud)


kch*_*kch 22

@hello代码中的第一个称为类实例变量.

它是常量Hello指向的类对象的实例变量.(这是该类的一个实例Class.)

从技术上讲,当你在class范围内时,你self被设置为当前类的对象,并且@variables与你当前的对象有关self.男孩我很蠢地解释这些事情.

通过观看The Pragmatic Programmers的每个5美元的截屏视频,您可以获得所有这些并且更加清晰.

(或者你可以在这里要求澄清,我会尝试更新.)


lfx*_*ool 10

在"红宝石编程语言"一书中有一个清晰的描述,阅读它将非常有帮助.我把它贴在这里(来自第7.1.16章):

在类定义中使用但在实例方法定义之外的实例变量类实例变量.

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
Run Code Online (Sandbox Code Playgroud)

......

因为类实例变量只是类对象的实例变量,所以我们可以使用attr,attr_reader和attr_accessor为它们创建访问器方法.

class << self
  attr_accessor :n, :totalX, :totalY
end
Run Code Online (Sandbox Code Playgroud)

通过定义这些访问器,我们可以将原始数据称为Point.n,Point.totalX和Point.totalY.


Tee*_*sti 5

我忘记了Ruby中有一个"类实例变量"概念.在任何情况下,OP的问题似乎都令人费解,并且在迄今为止的任何答案中都没有真正解决,除了kch的答案中的暗示:它是范围问题.(在编辑中添加:实际上,sris的答案确实在最后解决了这一点,但无论如何我都会让这个答案成立,因为我认为示例代码可能对理解问题很有帮助.)

在Ruby类中,以...开头的变量名称@可以引用两个变量之一:实例变量类实例变量,具体取决于它所引用的类中的位置.这是一个相当微妙的陷阱.

一个例子将阐明这一点.这是一个小的Ruby测试类(所有代码都在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
Run Code Online (Sandbox Code Playgroud)

我根据我的想法命名变量,尽管结果并非总是如此:

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
Run Code Online (Sandbox Code Playgroud)

@@class_variable@instance_variable总是表现为你所期望的:前者在类级别定义,并在一类方法或实例方法是否提到,它保存在顶部分配给它的价值.后者仅在类的对象中获取值T,因此在类方法中,它引用其值为的未知变量nil.

富有想象地命名的类方法按预期class_method输出值@@class_variable和两个@class_instance_variables,即在类的顶部初始化.然而,在实例方法initializeinstance_method,不同变量同名的访问,就是实例变量,不是类的实例变量.

您可以看到方法中的赋值initialize不会影响类实例变量@class_instance_variable_1,因为稍后调用class_method输出它的旧值,"WTF".相反,法initialize 宣布一个新的实例变量,其中一个是称为(误导)@class_instance_variable_1.分配给它的值"wtf"由方法initialize 和输出instance_method.

该变量@class_instance_variable_2在示例代码就相当于变量@hello在原来的问题:它的声明和初始化一个类的实例变量,但是当一个实例方法是指名称的变量,它实际上看到的实例变量具有相同的名称 -一个从未申报的,所以它的价值是零.