简体   繁体   English

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

[英]When do Ruby instance variables get set?

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

h = Hello.new
h.display

I created the class above. 我创建了上面的课程。 It doesn't print anything out. 它不打印任何东西。 I thought the instance variable @hello was set during the class declaration. 我认为在类声明期间设置了实例变量@hello。 But when I call the display method the output is 'nil'. 但是当我调用display方法时输出为'nil'。 What's the correct way to do this? 这样做的正确方法是什么?

Instance variables in ruby may be a bit confusing when first learning Ruby, especially if you are accustomed to another OO language like Java. 在第一次学习Ruby时,ruby中的实例变量可能有点令人困惑,特别是如果你习惯于另一种像Java这样的OO语言。

You cannot simply declare an instance variable. 您不能简单地声明实例变量。

One of the most important things to know about instance variables in ruby, apart from the notation with an @ sign prefix, is that they spring into life the first time they are assigned to . 除了带有@符号前缀的符号之外,了解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"]

You can use the method Object#instance_variables to list all instance variables of an object. 您可以使用Object#instance_variables方法列出Object#instance_variables所有实例变量。

You normally “declare” and initialize all the instance variables in the initialize method. 您通常在initize方法中“声明”并初始化所有实例变量。 Another way to clearly document which instance variables that should be publicly available is to use the Module methods attr_accessor (read/write), attr_writer (write) and attr_reader (read). 另一种清楚地记录应该公开可用的实例变量的方法是使用模块方法attr_accessor (读/写), attr_writer (写)和attr_reader (读)。 These methods will synthesize different accessor methods for the listed instance variable. 这些方法将为列出的实例变量合成不同的访问器方法。

class Hello
  attr_accessor :hello
end

h = Hello.new
p h.instance_variables 

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

The instance variable still isn't created until it's assigned to using the synthesized Hello#hello= method. 在将实例变量分配给使用合成的Hello#hello=方法之前,仍不会创建实例变量。

Another important issue, like kch described, is that you need to be aware of the different contexts active when declaring a class. 像kch所描述的另一个重要问题是,在声明一个类时,你需要了解不同的上下文。 When declaring a class the default receiver (self) in the outermost scope will be the object that represents the class itself. 声明类时,最外层作用域中的默认接收器(self)将是表示类本身的对象。 Hence your code will first create a class instance variable when assigning to @hello on the class level. 因此,在类级别分配给@hello时,您的代码将首先创建一个类实例变量。

Inside methods self will be the object on which the method is invoked, hence you are trying to print the value of an instance variable with the name @hello in the object, which doesn't exists (note that it's perfectly legal to read a non existing instance variable). 内部方法self将是调用该方法的对象,因此您尝试在对象中打印名称为@hello的实例变量的值,该值不存在(请注意,读取非对象是完全合法的)现有的实例变量)。

You need to add an initialize method: 您需要添加initialize方法:

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

h = Hello.new
h.display

The first @hello in your code is called a class instance variable. 代码中的第一个@hello称为类实例变量。

It's an instance variable of the class object which the constant Hello points to. 它是常量Hello指向的类对象的实例变量。 (and which is an instance of the class Class .) (以及类Class的实例。)

Technically, when you're within the class scope, your self is set to the object of your current class, and @variables pertain to your current self . 从技术上讲,当你在class范围内时,你的self被设置为当前类的对象,而@variables属于你当前的self Boy I suck at explaining these things. 男孩我很蠢地解释这些事情。

You can get all this and much more clarified to you by watching this collection of $5-each screencasts from The Pragmatic Programmers . 通过观看The Pragmatic Programmers的每个5美元的截屏视频,您可以获得所有这些并且更加清晰

(Or you can ask for clarifications here and I'll try to update.) (或者你可以在这里要求澄清,我会尝试更新。)

there is a clear description in the book "The ruby programming language", read it will be very helpful. 在“红宝石编程语言”一书中有一个清晰的描述,阅读它将非常有帮助。 I paste it here(from chapter 7.1.16): 我把它贴在这里(来自第7.1.16章):

An instance variable used inside a class definition but outside an instance method definition is a class instance variable . 在类定义中使用但在实例方法定义之外的实例变量类实例变量

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

...... ......

Because class instance variables are just instance variables of class objects, we can use attr, attr_reader, and attr_accessor to create accessor methods for them. 因为类实例变量只是类对象的实例变量,所以我们可以使用attr,attr_reader和attr_accessor为它们创建访问器方法。

class << self
  attr_accessor :n, :totalX, :totalY
end

With these accessors defined, we can refer to our raw data as Point.n, Point.totalX, and Point.totalY. 通过定义这些访问器,我们可以将原始数据称为Point.n,Point.totalX和Point.totalY。

I'd forgotten that there was a "class instance variable" concept in Ruby. 我忘记了Ruby中有一个“类实例变量”概念。 In any case, the OP's problem seemed puzzling, and wasn't really addressed in any of the answers heretofore, except for a hint in kch's answer: it's a problem of scope. 在任何情况下,OP的问题似乎都令人费解,并且在迄今为止的任何答案中都没有真正解决,除了kch的答案中的暗示:它是范围问题。 (Added on edit: Actually, sris's answer does address this point at the end, but I'll let this answer stand anyway, as I think the example code might be useful for understanding the problem.) (在编辑中添加:实际上,sris的答案确实在最后解决了这一点,但无论如何我都会让这个答案成立,因为我认为示例代码可能对理解问题很有帮助。)

In a Ruby class, a variable name starting with @ can refer to one of two variables: either to an instance variable or to a class instance variable , depending on where in the class it's referred to. 在Ruby类中,以@开头的变量名称可以引用两个变量之一: 实例变量类实例变量 ,具体取决于它所引用的类中的位置。 This is a fairly subtle gotcha. 这是一个相当微妙的陷阱。

An example will clarify the point. 一个例子将阐明这一点。 Here's a little Ruby test class (all code tested in irb): 这是一个小的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

I named the variables according to what I thought they were, though that turns out to not to always be the case: 我根据我的想法命名变量,尽管结果并非总是如此:

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

The @@class_variable and @instance_variable always behave as you'd expect: the former is defined on the class level, and whether referred to in a class method or in an instance method, it holds value assigned to it at the top. @@class_variable@instance_variable总是按照您的预期运行:前者是在类级别定义的,无论是在类方法还是在实例方法中引用,它都保存在顶部分配给它的值。 The latter only gets a value in an object of class T , so in a class method, it refers to an unknown variable whose value is nil . 后者仅在类T的对象中获取值,因此在类方法中,它引用其值为nil的未知变量。

The class method imaginatively named class_method outputs the values of @@class_variable and the two @class_instance_variable s as expected, that is, as initialized at the top of the class. 富有想象力地命名为class_method的类方法按预期输出@@class_variable和两个@class_instance_variable的值,即在类的顶部初始化。 However, in the instance methods initialize and instance_method , different variables of the same name are accessed, that is, instance variables, not class instance variables . 但是,在实例方法initializeinstance_method ,访问了同名的 不同变量,即实例变量,而不是类实例变量

You can see that the assignment in the initialize method did not affect the class instance variable @class_instance_variable_1 , because the later call of class_method outputs its old value, "WTF" . 您可以看到initialize方法中的赋值不会影响类实例变量@class_instance_variable_1 ,因为稍后调用class_method输出其旧值"WTF" Instead, method initialize declared a new instance variable, one which is also named (misleadingly) @class_instance_variable_1 . 相反,方法initialize 声明了一个新的实例变量,变量 被命名为(误导性) @class_instance_variable_1 The value assigned to it, "wtf" , is output by methods initialize and instance_method . 分配给它的值"wtf"由方法initializeinstance_method输出。

The variable @class_instance_variable_2 in the example code is equivalent to variable @hello in the original problem: it's declared and initialized as a class instance variable, but when an instance method refers to a variable of that name, it actually sees an instance variable with the same name -- one which was never declared, so its value is nil. 示例代码中的变量@class_instance_variable_2等同于原始问题中的变量@hello :它被声明并初始化为类实例变量,但是当实例方法引用该名称的变量时,它实际上看到了一个实例变量同名 - 一个从未声明的名称,因此其值为零。

I'd also recommend looking at class variables which are prefixed with "@@" - here's some sample code to show you how class and instance vars are different: 我还建议查看以“@@”为前缀的类变量 - 这里有一些示例代码,向您展示类和实例变量是如何不同的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM