我很难理解Ruby中的attr_accessor。 有人能给我解释一下吗?


当前回答

我是ruby的新手,不得不理解下面的奇怪之处。也许将来能帮到别人。最后,就像上面提到的,其中两个函数(def myvar, def myvar=)都隐式地访问@myvar,但是这些方法可以被局部声明覆盖。

class Foo
  attr_accessor 'myvar'
  def initialize
    @myvar = "A"
    myvar = "B"
    puts @myvar # A
    puts myvar # B - myvar declared above overrides myvar method
  end

  def test
    puts @myvar # A
    puts myvar # A - coming from myvar accessor

    myvar = "C" # local myvar overrides accessor
    puts @myvar # A
    puts myvar # C

    send "myvar=", "E" # not running "myvar =", but instead calls setter for @myvar
    puts @myvar # E
    puts myvar # C
  end
end

其他回答

Attr_accessor只是一个方法。(这个链接应该提供更多关于它如何工作的见解——看看生成的方法对,一个教程应该告诉你如何使用它。)

关键在于,类在Ruby中不是定义(在c++和Java等语言中“只是定义”),而是求值的表达式。在此求值期间,调用attr_accessor方法,该方法反过来修改当前类——记住隐式接收器:self。其中self是此时“打开”的类对象。

对attr_accessor和friends的需求是:

像Smalltalk一样,Ruby不允许在该对象的method1之外访问实例变量。也就是说,不能像在Java甚至Python中那样以x.y形式访问实例变量。在Ruby中,y总是被当作一个要发送的消息(或“要调用的方法”)。因此attr_*方法创建包装器,通过动态创建的方法代理实例@变量访问。 样板很糟糕

希望这能澄清一些细节。快乐的编码。


1严格来说,这并不正确,有一些“技术”围绕这一点,但没有语法支持“公共实例变量”访问。

我是ruby的新手,不得不理解下面的奇怪之处。也许将来能帮到别人。最后,就像上面提到的,其中两个函数(def myvar, def myvar=)都隐式地访问@myvar,但是这些方法可以被局部声明覆盖。

class Foo
  attr_accessor 'myvar'
  def initialize
    @myvar = "A"
    myvar = "B"
    puts @myvar # A
    puts myvar # B - myvar declared above overrides myvar method
  end

  def test
    puts @myvar # A
    puts myvar # A - coming from myvar accessor

    myvar = "C" # local myvar overrides accessor
    puts @myvar # A
    puts myvar # C

    send "myvar=", "E" # not running "myvar =", but instead calls setter for @myvar
    puts @myvar # E
    puts myvar # C
  end
end

我认为让新手和程序员(比如我自己)困惑的部分原因是:

“为什么我不能告诉实例它有任何给定的属性(例如,名称),并一次性为该属性赋值?”

更一般化一点,但这就是我的想法:

考虑到:

class Person
end

我们还没有将Person定义为可以有名称或任何其他属性的东西。

那么如果我们

baby = Person.new

...试着给它们起个名字…

baby.name = "Ruth"

我们得到一个错误,因为在Rubyland中,对象的Person类不是与“名称”相关联或不具有“名称”的东西……然而!

但是我们可以使用任何给定的方法(参见前面的回答)来说明,“Person类(baby)的实例现在可以有一个名为'name'的属性,因此我们不仅有了获取和设置该名称的语法方法,而且这样做对我们来说是有意义的。”

再一次,从一个稍微不同和更一般的角度来解决这个问题,但我希望这有助于Person类的下一个实例找到这个线程的方法。

Attr_accessor(如@pst所述)只是一个方法。它的作用是为你创建更多的方法。

这段代码:

class Foo
  attr_accessor :bar
end

等价于下面的代码:

class Foo
  def bar
    @bar
  end
  def bar=( new_value )
    @bar = new_value
  end
end

你可以自己用Ruby编写这样的方法:

class Module
  def var( method_name )
    inst_variable_name = "@#{method_name}".to_sym
    define_method method_name do
      instance_variable_get inst_variable_name
    end
    define_method "#{method_name}=" do |new_value|
      instance_variable_set inst_variable_name, new_value
    end
  end
end

class Foo
  var :bar
end

f = Foo.new
p f.bar     #=> nil
f.bar = 42
p f.bar     #=> 42

我也遇到过这个问题,并写了一篇有点冗长的回答。关于这个问题已经有了一些很好的答案,但如果有人想进一步澄清,我希望我的答案能有所帮助

初始化方法

Initialize允许您在创建实例时将数据设置为对象的实例,而不是每次创建类的新实例时都必须在代码中的单独一行中设置数据。

class Person

  def initialize(name)
    @name = name
  end


  def greeting
    "Hello #{@name}"
  end
end

person = Person.new("Denis")
puts person.greeting

在上面的代码中,我们使用initialize方法通过initialize中的参数传递Dennis来设置名称“Denis”。如果我们想在不使用initialize方法的情况下设置名称,可以这样做:

class Person
  attr_accessor :name

  # def initialize(name)
  #     @name = name
  # end

  def greeting
    "Hello #{name}"
  end
end

person = Person.new
person.name = "Dennis"
puts person.greeting

在上面的代码中,我们使用person.name调用attr_accessor setter方法来设置名称,而不是在初始化对象时设置值。

这两个“方法”都做这个工作,但初始化节省了我们的时间和代码行数。

这是初始化的唯一工作。不能将initialize作为方法调用。要实际获取实例对象的值,需要使用getter和setter (attr_reader (get)、attr_writer(set)和attr_accessor(两者))。请参阅下面的详细信息。

getter, Setters (attr_reader, attr_writer, attr_accessor)

getter, attr_reader: getter的全部目的是返回特定实例变量的值。请访问下面的示例代码以了解这方面的详细信息。

class Item

  def initialize(item_name, quantity)
    @item_name = item_name
    @quantity = quantity
  end

  def item_name
    @item_name
  end

  def quantity
     @quantity
  end
end

example = Item.new("TV",2)
puts example.item_name
puts example.quantity

在上面的代码中,您在Item“example”的实例上调用方法“item_name”和“quantity”。“举例”。Item_name和example。Quantity”将返回(或“获取”)传递到“示例”中的参数值,并将它们显示在屏幕上。

幸运的是,在Ruby中有一个固有的方法,可以让我们更简洁地编写代码;attr_reader方法。请参阅下面的代码;

class Item

attr_reader :item_name, :quantity

  def initialize(item_name, quantity)
    @item_name = item_name
    @quantity = quantity
  end

end

item = Item.new("TV",2)
puts item.item_name
puts item.quantity

这种语法的工作方式完全相同,只是它为我们节省了6行代码。想象一下,如果您有5个可归属于Item类的状态?代码很快就会变长。

Setters, attr_writer: setter方法最初让我感到困惑的是,在我看来,它执行的功能与initialize方法相同。下面我将根据我的理解来解释两者的区别;

如前所述,initialize方法允许您在对象创建时设置对象实例的值。

但是,如果您想稍后(在实例创建之后)设置这些值,或者在初始化后更改它们,该怎么办呢?这是一个使用setter方法的场景。这就是区别。在最初使用attr_writer方法时,不必“设置”特定的状态。

下面的代码是使用setter方法为Item类的这个实例声明值item_name的示例。注意,我们继续使用getter方法attr_reader,这样我们就可以获取值并将它们打印到屏幕上,以防您想自己测试代码。

class Item

attr_reader :item_name

  def item_name=(str)
    @item_name = (str)
  end

end

下面的代码是使用attr_writer来再次缩短代码并节省时间的示例。

class Item

attr_reader :item_name
attr_writer :item_name

end

item = Item.new
puts item.item_name = "TV"

下面的代码重复了上面的初始化示例,其中我们使用initialize在创建时设置对象item_name的值。

class Item

attr_reader :item_name

  def initialize(item_name)
    @item_name = item_name
  end

end

item = Item.new("TV")
puts item.item_name

attr_accessor:执行attr_reader和attr_writer的函数,为您节省了多一行代码。