什么是Ruby中的attr_accessor?

时间:2010-12-06 21:07:06

标签: ruby

我很难理解Ruby中的attr_accessor。有人可以向我解释一下吗?

20 个答案:

答案 0 :(得分:2202)

假设你有一个班级Person

class Person
end

person = Person.new
person.name # => no method error

显然我们从未定义方法name。我们这样做。

class Person
  def name
    @name # simply returning an instance variable @name
  end
end

person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error

啊哈,我们可以读取名称,但这并不意味着我们可以指定名称。这是两种不同的方法。前者称为 reader ,后者称为 writer 。我们还没有创作作家,所以让我们这样做。

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"

真棒。现在我们可以使用reader和writer方法编写和读取实例变量@name。除此之外,经常这样做,为什么每次浪费时间写这些方法呢?我们可以更轻松地做到这一点。

class Person
  attr_reader :name
  attr_writer :name
end

即使这样也会重复。当你想要读者和作者都只使用访问者时!

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"

以同样的方式工作!并猜测:我们的person对象中的实例变量@name将被设置为就像我们手动执行时一样,因此您可以在其他方法中使用它。

class Person
  attr_accessor :name

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

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"

就是这样。要了解attr_readerattr_writerattr_accessor方法如何为您实际生成方法,请阅读其他答案,书籍,ruby文档。

答案 1 :(得分:121)

attr_accessor 只是一种方法。 (该链接应提供有关其工作原理的更多信息 - 查看生成的方法对,教程应向您展示如何使用它。)

诀窍是class 不是Ruby中的定义(它只是C ++和Java等语言中的“定义”),但它是一个表达式求值。在此评估期间,调用attr_accessor方法,然后修改当前类 - 记住隐式接收器:self.attr_accessor,其中self是此时的“开放”类对象

attr_accessor和朋友的需求是:

  1. Ruby与Smalltalk一样,不允许在该对象的 1 方法之外访问实例变量。也就是说,无法以x.y形式访问实例变量,例如Java或甚至Python。在Ruby中y始终被视为要发送的消息(或“调用方法”)。因此,attr_*方法创建包装器,通过动态创建的方法代理实例@variable访问。

  2. Boilerplate糟透了

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


    1 这不是严格正确且有some "techniques" around this,但没有对“公共实例变量”访问的语法支持。

答案 2 :(得分:64)

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

答案 3 :(得分:36)

attr_accessor非常简单:

attr_accessor :foo

是:

的快捷方式
def foo=(val)
  @foo = val
end

def foo
  @foo
end

它只不过是对象的getter / setter

答案 4 :(得分:21)

基本上他们伪造了可公开访问的数据属性,Ruby没有。

答案 5 :(得分:17)

它只是一个为实例变量定义getter和setter方法的方法。一个示例实现是:

def self.attr_accessor(*names)
  names.each do |name|
    define_method(name) {instance_variable_get("@#{name}")} # This is the getter
    define_method("#{name}=") {|arg| instance_variable_set("@#{name}", arg)} # This is the setter
  end
end

答案 6 :(得分:13)

如果您熟悉OOP概念,则必须熟悉getter和setter方法。 attr_accessor在Ruby中做同样的事情。

一般方式的Getter和Setter

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Eshaan'
person.name # => "Eshaan"

Setter Method

def name=(val)
  @name = val
end

Getter方法

def name
  @name
end

Ruby中的Getter和Setter方法

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"

答案 7 :(得分:13)

没有任何代码的简单说明

上述大多数答案都使用代码。这个解释试图通过一个类比/故事来回答它,而不是使用任何一个:

外部各方无法访问内部CIA秘密

  • 让我们想象一个真正秘密的地方:中央情报局。除了中央情报局内部的人员,没有人知道中央情报局发生了什么。换句话说,外部人员无法访问中央情报局的任何信息。但是因为组织完全保密并不好,所以外界也可以获得某些信息 - 只有CIA希望每个人都知道的事情:例如:中央情报局局长,这个部门与所有其他政府部门的环保程度如何。其他信息:例如:谁是伊拉克或阿富汗的秘密特工 - 这些类型的事情可能会在接下来的150年里保密。

  • 如果您不在CIA之外,则只能访问向公众提供的信息。或者,要使用CIA说法,您只能访问"已清除的信息"。

  • CIA希望在CIA之外向公众提供的信息称为:属性。

读写属性的含义:

  • 在CIA的情况下,大多数属性都是"只读"。这意味着如果您是CIA的派对外部,您可以询问:"谁是CIA的主管?"你会得到一个直接的答案。但是你不能做什么"只读"属性是在CIA中进行更改。例如你不能打个电话突然决定你希望金·卡戴珊成为导演,或者你希望帕丽斯·希尔顿成为总司令。

  • 如果属性给你"写"如果您愿意,即使您在外面也可以进行访问。否则,你唯一能做的就是阅读。

    换句话说,访问者允许您对不允许外部人员进入的组织进行查询或进行更改,具体取决于访问者是读取还是写入访问者。

班级内的对象可以轻松互相访问

  • 另一方面,如果您已经在CIA内部,那么您可以轻松地在喀布尔调用您的CIA操作员,因为这些信息很容易获取,因为您已经在里面。但是,如果你在外面 CIA,你将无法获得访问权限:你将无法知道他们是谁(阅读权限),你将无法改变他们的任务(写访问)。

与类完全相同,并且能够访问其中的变量,属性和方法。 HTH!有任何问题,请询问,我希望我能澄清一下。

答案 8 :(得分:12)

我也遇到了这个问题,并对这个问题写了一个冗长的答案。已经有一些很好的答案,但任何寻求更多澄清的人,我希望我的答案可以帮助

初始化方法

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

class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end


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

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

在上面的代码中,我们使用initialize方法设置名称“Denis”,方法是将Dennis传递给Initialize中的参数。如果我们想在没有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方法来设置名称,而不是在初始化对象时设置值。

执行此操作的两种“方法”,但初始化节省了我们的时间和代码行。

这是初始化的唯一工作。您无法调用初始化作为方法。要实际获取实例对象的值,您需要使用getter和setter(attr_reader(get),attr_writer(set)和attr_accessor(both))。有关这些内容的详细信息,请参见下文。

Getters,Setters(attr_reader,attr_writer,attr_accessor)

Getters,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”。 “puts example.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

这种语法的工作方式完全相同,只为它节省了六行代码。想象一下,如果你还有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的功能,为您节省一行代码。

答案 9 :(得分:10)

我认为让新Rubyists /程序员(比如我自己)感到困惑的一部分是:

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

稍微宽泛一点,但这就是它为我点击的方式:

假设:

class Person
end

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

所以如果我们那么:

baby = Person.new

...并尝试给他们起个名字......

baby.name = "Ruth"

我们得到错误,因为在Rubyland中,Person类的对象不是与“名称”关联或能够拥有“名称”的东西......

但是我们可以使用任何给定的方法(参见前面的答案)作为一种方式来说,“Person类的实例(baby现在可以具有一个名为的属性'name',因此我们不仅有一种获取和设置该名称的语法方法,但我们这样做是有道理的。“

再一次,从一个稍微不同且更一般的角度来看这个问题,但我希望这有助于下一个类人物的实例,他们找到了通往这个线程的方法。

答案 10 :(得分:7)

简单地说,它将为该类定义一个setter和getter。

请注意

attr_reader :v is equivalant to 
def v
  @v
end

attr_writer :v is equivalant to
def v=(value)
  @v=value
end

所以

attr_accessor :v which means 
attr_reader :v; attr_writer :v 

等同于为类定义setter和getter。

答案 11 :(得分:5)

只需attr-accessor为指定的属性创建gettersetter方法

答案 12 :(得分:4)

理解它的另一种方法是通过attr_accessor找出它消除的错误代码。

示例:

class BankAccount    
  def initialize( account_owner )
    @owner = account_owner
    @balance = 0
  end

  def deposit( amount )
    @balance = @balance + amount
  end

  def withdraw( amount )
    @balance = @balance - amount
  end
end

可以使用以下方法:

$ bankie = BankAccout.new("Iggy")
$ bankie 
$ bankie.deposit(100)
$ bankie.withdraw(5)

以下方法抛出错误:

$ bankie.owner     #undefined method `owner'... 
$ bankie.balance   #undefined method `balance'...
从技术上讲,

ownerbalance不是方法,而是属性。 BankAccount类没有def ownerdef balance。如果是,则可以使用以下两个命令。但这两种方法都不存在。但是,您可以访问属性,就像您通过attr_accessor 访问方法一样! 因此单词attr_accessor 。属性。访问。它访问属性,就像访问方法一样。

添加attr_accessor :balance, :owner可让您读写balanceowner"方法"。现在你可以使用最后两种方法了。

$ bankie.balance
$ bankie.owner

答案 13 :(得分:2)

为此模块定义命名属性,其名称为symbol.id2name,创建实例变量(@name)以及相应的访问方法以读取它。还创建一个名为name =的方法来设置属性。

module Mod
  attr_accessor(:one, :two)
end
Mod.instance_methods.sort   #=> [:one, :one=, :two, :two=]

答案 14 :(得分:1)

总结属性访问器aka attr_accessor为您提供了两种免费方法。

就像在Java中一样,它们被称为getter和setter。

许多答案都展示了很好的例子,所以我只是简短的。

#the_attribute

#the_attribute =

在旧的ruby文档中,哈希标记#表示方法。 它还可以包含一个类名前缀...... MyClass的#my_method

答案 15 :(得分:0)

属性和访问方法

属性是可以从对象外部访问的类组件。它们在许多其他编程语言中称为属性。可以使用“点表示法”访问它们的值,如object_name.attribute_name中所示。与Python和其他一些语言不同,Ruby不允许直接从对象外部访问实例变量。

class Car
  def initialize
    @wheels = 4  # This is an instance variable
  end
end

c = Car.new
c.wheels     # Output: NoMethodError: undefined method `wheels' for #<Car:0x00000000d43500>

在上面的例子中,c是Car类的实例(对象)。我们尝试从对象外部读取wheel实例变量的值,但未成功。发生的事情是Ruby试图在c对象中调用一个名为wheels的方法,但是没有定义这样的方法。简而言之,object_name.attribute_name尝试在对象中调用名为attribute_name的方法。要从外部访问wheels变量的值,我们需要通过该名称实现一个实例方法,该方法将在调用时返回该变量的值。这被称为存取方法。在通用编程上下文中,从对象外部访问实例变量的常用方法是实现访问器方法,也称为getter和setter方法。 getter允许从外部读取类中定义的变量的值,并且setter允许从外部写入。

在下面的示例中,我们将getter和setter方法添加到Car类,以从对象外部访问wheel变量。这不是定义getter和setter的“Ruby方式”;它仅用于说明getter和setter方法的作用。

class Car
  def wheels  # getter method
    @wheels
  end

  def wheels=(val)  # setter method
    @wheels = val
  end
end

f = Car.new
f.wheels = 4  # The setter method was invoked
f.wheels  # The getter method was invoked
# Output: => 4

以上示例有效,类似的代码通常用于在其他语言中创建getter和setter方法。但是,Ruby提供了一种更简单的方法:三种内置方法,称为attr_reader,attr_writer和attr_acessor。 attr_reader方法使实例变量可以从外部读取,attr_writer使其可写,而attr_acessor使其可读和可写。

上面的例子可以像这样重写。

class Car
  attr_accessor :wheels
end

f = Car.new
f.wheels = 4
f.wheels  # Output: => 4

在上面的示例中,wheels属性将从对象外部读取和写入。如果不是attr_accessor,我们使用attr_reader,它将是只读的。如果我们使用attr_writer,它将是只写的。这三种方法本身不是getter和setter,但是在调用时,它们为我们创建了getter和setter方法。它们是动态(以编程方式)生成其他方法的方法;这叫做元编程。

第一个(更长的)示例,不使用Ruby的内置方法,只应在getter和setter方法中需要其他代码时使用。例如,在为实例变量赋值之前,setter方法可能需要验证数据或进行一些计算。

可以使用instance_variable_get和instance_variable_set内置方法从对象外部访问(读取和写入)实例变量。然而,这很少是合理的,而且通常是一个坏主意,因为绕过封装往往会造成各种各样的破坏。

答案 16 :(得分:0)

我是红宝石的新手,只需要处理以下奇怪的事情。将来可能会帮助别人。最后,就像上面提到的那样,其中两个函数(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

答案 17 :(得分:0)

尽管存在大量现有答案,但在我看来,没有一个答案可以解释此处涉及的实际机制。这是元编程;它利用了以下两个事实:

  • 您可以即时修改模块/类

  • 模块/类声明本身就是可执行代码

好的,那么想象一下:

class Nameable
  def self.named(whatvalue)
    define_method :name do whatvalue end
  end
end

我们声明了一个类方法 named,当用一个值调用它时,创建一个实例方法,名为name,它返回那个值.这就是元编程部分。

现在我们将继承该类:

class Dog < Nameable
  named "Fido"
end

我们到底做了什么?好吧,在类声明中,可执行代码参照类执行。所以裸词 named 实际上是对类方法 named 的调用,它是我们从 Nameable 继承的;我们将字符串 "Fido" 作为参数传递。

类方法 named 有什么作用?它创建了一个名为 name 的实例方法,该方法返回该值。所以现在,在幕后,Dog 有一个看起来像这样的方法:

def name
   "Fido"
end

不相信我?然后看这个小动作:

puts Dog.new.name #=> Fido

我为什么要告诉你这些?因为我刚刚用 named 为 Nameable 所做的几乎与 attr_accessor 为 Module 所做的一样。当您说 attr_accessor 时,您正在调用创建实例方法的类方法(继承自 Module)。特别是,它为您提供的名称作为参数的实例属性创建了一个 getter 和 setter 方法,这样您就不必自己编写这些 getter 和 setter 方法。

答案 18 :(得分:-2)

Hmmm. Lots of good answers. Here is my few cents on it.

  • attr_accessor is a simple method that helps us in cleaning(DRY-ing) up the repeating getter and setter methods.

  • So that we can focus more on writing business logic and not worry about the setters and getters.

答案 19 :(得分:-3)

attr_accessor 的主要功能是从其他文件访问数据的能力。
所以你通常会有attr_reader或attr_writer,但好消息是Ruby允许你将这两者与attr_accessor结合起来。我认为它是 我的方法 ,因为它更圆润或多功能。 另外,请记住,在Rails中,这被消除了,因为它在后端为您完成。所以换句话说:你最好使用attr_acessor而不是其他两个,因为你不必担心具体,访问者会覆盖它。我知道这更多是一般性的解释,但它帮助我作为初学者。

希望这有帮助!