簡體   English   中英

什么時候Ruby實例變量設置?

[英]When do Ruby instance variables get set?

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

h = Hello.new
h.display

我創建了上面的課程。 它不打印任何東西。 我認為在類聲明期間設置了實例變量@hello。 但是當我調用display方法時輸出為'nil'。 這樣做的正確方法是什么?

在第一次學習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"]

您可以使用Object#instance_variables方法列出Object#instance_variables所有實例變量。

您通常在initize方法中“聲明”並初始化所有實例變量。 另一種清楚地記錄應該公開可用的實例變量的方法是使用模塊方法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"]

在將實例變量分配給使用合成的Hello#hello=方法之前,仍不會創建實例變量。

像kch所描述的另一個重要問題是,在聲明一個類時,你需要了解不同的上下文。 聲明類時,最外層作用域中的默認接收器(self)將是表示類本身的對象。 因此,在類級別分配給@hello時,您的代碼將首先創建一個類實例變量。

內部方法self將是調用該方法的對象,因此您嘗試在對象中打印名稱為@hello的實例變量的值,該值不存在(請注意,讀取非對象是完全合法的)現有的實例變量)。

您需要添加initialize方法:

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

h = Hello.new
h.display

代碼中的第一個@hello稱為類實例變量。

它是常量Hello指向的類對象的實例變量。 (以及類Class的實例。)

從技術上講,當你在class范圍內時,你的self被設置為當前類的對象,而@variables屬於你當前的self 男孩我很蠢地解釋這些事情。

通過觀看The Pragmatic Programmers的每個5美元的截屏視頻,您可以獲得所有這些並且更加清晰

(或者你可以在這里要求澄清,我會嘗試更新。)

在“紅寶石編程語言”一書中有一個清晰的描述,閱讀它將非常有幫助。 我把它貼在這里(來自第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

......

因為類實例變量只是類對象的實例變量,所以我們可以使用attr,attr_reader和attr_accessor為它們創建訪問器方法。

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

通過定義這些訪問器,我們可以將原始數據稱為Point.n,Point.totalX和Point.totalY。

我忘記了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

我根據我的想法命名變量,盡管結果並非總是如此:

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@instance_variable總是按照您的預期運行:前者是在類級別定義的,無論是在類方法還是在實例方法中引用,它都保存在頂部分配給它的值。 后者僅在類T的對象中獲取值,因此在類方法中,它引用其值為nil的未知變量。

富有想象力地命名為class_method的類方法按預期輸出@@class_variable和兩個@class_instance_variable的值,即在類的頂部初始化。 但是,在實例方法initializeinstance_method ,訪問了同名的 不同變量,即實例變量,而不是類實例變量

您可以看到initialize方法中的賦值不會影響類實例變量@class_instance_variable_1 ,因為稍后調用class_method輸出其舊值"WTF" 相反,方法initialize 聲明了一個新的實例變量,變量 被命名為(誤導性) @class_instance_variable_1 分配給它的值"wtf"由方法initializeinstance_method輸出。

示例代碼中的變量@class_instance_variable_2等同於原始問題中的變量@hello :它被聲明並初始化為類實例變量,但是當實例方法引用該名稱的變量時,它實際上看到了一個實例變量同名 - 一個從未聲明的名稱,因此其值為零。

我還建議查看以“@@”為前綴的類變量 - 這里有一些示例代碼,向您展示類和實例變量是如何不同的:

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