Ruby中的attr_accessor是什么?


1022

我有一个很难理解attr_accessor红宝石
谁可以给我解释一下这个?



1
attr_accessor是否在Git中以相同的方式工作?我发现有些教程解释不充分,而另一些教程则具有先验知识。
Angelfirenze 2014年

10
@Angelfirenze,git与无关attr_accessor。Git是一个版本控制软件,而在Ruby中attr_accessor是一个方法
乌兹别克斯坦'16

Answers:


2358

假设您有一堂课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

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

class Person
  def name
    @name
  end

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

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

太棒了 现在我们可以@name使用reader和writer方法编写和读取实例变量。除非这样做如此频繁,为什么每次都要浪费时间编写这些方法?我们可以更轻松地做到这一点。

class Person
  attr_reader :name
  attr_writer :name
end

即使这样也可以重复。当您同时希望读者和作家都使用访问器时!

class Person
  attr_accessor :name
end

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

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

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方法实际上为您生成方法,阅读其他的答案,书,红宝石文档。


46
@hakunin-谢谢您的明确答复。我缺少的是为什么Ruby语法在attr_ *语句中为实例变量建议一个冒号':'?似乎更直接地使用与类中其他地方引用的变量相同的'@'语法。
2012年

207
@WilliamSmith要回答您的问题,您需要了解这attr_accessor是在当前类上调用的方法,并且:name是您传递给该方法的参数。这不是一种特殊的语法,而是一个简单的方法调用。如果您要给它@name变量,那就没有意义了,因为@name将包含nil。所以这就像写作attr_accessor nil。您没有传递给它需要创建的变量,而是传递了要调用该变量的名称。
马克斯·切尔纳克

23
@hakunin-完全有道理。我今天才刚刚得知,ruby在解析文件时实际上正在“运行”,并且每个语句和表达式实际上都是对某个对象的方法调用。包括attr_accessor。非常有帮助。
2012年

52
用过Rails 3年了,甚至从未意识到这一点。羞耻
肖恩·肖

5
@Buminda是的,但是方法name和变量@name不是同一回事。不要混淆他们。您的类中有实例变量@name,并且定义attr_reader :name为可以从外部读取它。没有attr_reader没有简单的方法,您就可以@name在课堂之外进行访问。
Max Chernyak

127

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

诀窍是,class不是定义在Ruby中(它是在像C ++和Java语言“只是一个定义”),但它是一个用于评估表达。正是在评估期间,attr_accessor调用该方法的方法又修改了当前类-记住隐式接收器:self.attr_accessorself此时“打开”类对象在哪里。

attr_accessor和朋友的需求很好:

  1. 像Smalltalk一样,Ruby不允许在该对象的方法1之外访问实例变量。也就是说,实例变量不能以x.yJava甚至Python中常见的形式访问。在Ruby y中,始终将其作为发送消息(或“调用方法”)。因此,这些attr_*方法创建了包装器,这些包装器@variable通过动态创建的方法代理实例访问。

  2. 样板糟透了

希望这可以澄清一些小细节。快乐的编码。


1严格来说,这不是真的,并且周围一些“技术”,但是不支持“公共实例变量”访问的语法。


当您说attr_accessor是“只是一种方法”时,我明白了。但是调用该方法所用的语法是什么?我无法在ruby文档中找到谈论诸如some_method:name =>“ whatever”,:notherName,:etc之类的语法的部分
BT

68

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
这是一个很好的例子,说明即使在最初学者的情况下也使用元编程。非常好。
约翰·西蒙

2
我一直在寻找的实现草图,attr_accessor终于在这里找到了!尽管它解决了我的问题,但是我很好奇我可以在哪里找到这样的实现示例?
瓦西夫·侯赛因

40

attr_accessor 很简单:

attr_accessor :foo

是以下操作的快捷方式:

def foo=(val)
  @foo = val
end

def foo
  @foo
end

它只不过是对象的获取器/设置器


10
你的回答很好。根据我的字典,“快捷方式”是指“更短的替代路线”,而不是“语法糖”或“由翻译人员解释的宏”。
bowsersenior 2010年

25

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


4
尽管此评论并不完全有用,但确实如此。强调了Ruby中“ get”方法之外不存在公共数据属性的事实,这对于尝试学习该语言的人来说确实是有用的信息。
埃里克·丹德

3
这确实不应该被否决。作为一个非Ruby人士,他试图弄清楚这些东西,这个答案非常有帮助!
Brad 2013年

1
同意,似乎与C#的名字非常相似{get; 集;}
David Miler 2013年

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

用这种方式处理多个属性真是太好了!
瓦西夫·侯赛因

这是一个非常有用的代码段,可以解决我与元编程相关的另一个问题。
alexventuraio

15

无需任何代码的简单说明

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

外部团体无法访问CIA内部机密

  • 让我们想象一个真正秘密的地方:中央情报局。除了中央情报局内部的人员,没人知道中央情报局正在发生什么。换句话说,外部人员无法访问CIA中的任何信息。但是因为拥有一个完全秘密的组织并没有好处,所以某些信息可以提供给外界-当然只有CIA希望每个人都知道的事情:例如CIA的局长,比较该部门对环境的友好程度其他信息:例如谁是伊拉克或阿富汗的秘密特工-这些事情在未来150年中可能仍然是秘密。

  • 如果您不在中央情报局范围内,则只能访问它向公众公开的信息。或者,要使用CIA的说法,您只能访问“已清除”的信息。

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

读写属性的含义:

  • 对于CIA,大多数属性是“只读”的。这意味着,如果你是一个党的对外中情局,你可以问: “谁是中情局的局长?” 您会得到一个直接的答案。但是,使用“只读”属性不能做的是在CIA中进行更改更改。例如,您无法打个电话,突然决定让金·卡戴珊(Kim Kardashian)担任导演,或者让帕里斯·希尔顿(Paris Hilton)担任总司令。

  • 如果属性赋予您“写”访问权限,则即使您不在,也可以进行更改。否则,您唯一可以做的就是阅读。

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

类内的对象可以轻松地相互访问

  • 另一方面,如果您已经 CIA 内部,则可以轻松地致电喀布尔的CIA特工,因为如果您已经在内部,则可以轻松访问此信息。但是,如果您不在 CIA范围内,则根本不会获得访问权限:您将无法知道他们是谁(读取访问权限),也将无法更改其任务(写入访问权限)。

类和您访问类中的变量,属性和方法的能力完全一样。HTH!任何问题,请问,希望我能澄清。


您的解释很有道理!+1对不起,您确定“由中央情报局(CIA)清除的信息正确吗?”
kouty 2016年

CIA中有各种“清除”级别:例如,最高机密(除了Prez之外没有其他人)或公众信任(每个人都可以阅读该信息)。中央情报局实际上提供了很多非常酷的事实!
BKSpurgeon

您仅应为Kardashian,Paris Hilton的例子投票就可以了:)我认为特朗普对总统来说已经很糟糕了,想象这两个负责人!
rmcsharry19年

是! 这就是我们需要的,没有代码的StackOverflow!:-)
Marvin

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"

设置方法

def name=(val)
  @name = val
end

吸气方法

def name
  @name
end

Ruby中的Getter和Setter方法

class Person
  attr_accessor :name
end

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

2
完美的解释!这是非常方便的行为,很容易被覆盖。
Rubyrider

12

我也面临这个问题,并为这个问题写了一个冗长的答案。已经有了一些很好的答案,但是任何需要进一步澄清的人,希望我的答案可以为您提供帮助

初始化方法

初始化允许您在创建实例时将数据设置到对象的实例,而不必每次创建类的新实例时都将它们设置在代码的单独一行上。

class Person

  def initialize(name)
    @name = name
  end


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

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

在上面的代码中,我们通过使用Initialize中的参数传递Dennis来使用initialize方法设置名称“ 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(both))。有关这些的更多详细信息,请参见下文。

Getters,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

在上面的代码中,您在项目“ example”的实例上调用方法“ item_name”和“ quantity”。“ puts example.item_name”和“ example.quantity”将返回(或“获取”)传递给“ example”的参数的值并将其显示在屏幕上。

幸运的是,在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"

下面的代码是上面初始化示例的重申,在上面的示例中,我们使用初始化在创建时设置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的功能,为您节省一行代码。


10

我认为使新的Rubyists /程序员(像我自己)感到困惑的部分原因是:

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

更加概括,但这就是它为我所点击的方式:

鉴于:

class Person
end

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

因此,如果我们那么:

baby = Person.new

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

baby.name = "Ruth"

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

但是我们可以使用任何给定的方法(请参阅前面的答案)来表示:“一个Person类(baby)的实例现在可以具有一个名为'name'的属性,因此,我们不仅有一种语法上的获取和设置该名称,但对我们而言这样做很有意义。”

同样,从稍微不同且更笼统的角度提出这个问题,但我希望这对下一个找到此线程的Person类的下一个实例有所帮助。


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。


5

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


5

理解它的另一种方法是找出具有可以消除哪些错误代码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

2

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

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

1

总结一个属性访问器,又名attr_accessor给您两个免费的方法。

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

许多答案都给出了很好的例子,所以我只是简单地讲一下。

#the_attribute

#the_attribute =

在旧的ruby文档中,井号#表示方法。它也可以包含一个类名前缀... MyClass#my_method


1

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

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对象中调用一个名为wheel的方法,但未定义此类方法。简而言之,object_name.attribute_name尝试在对象内调用一个名为attribute_name的方法。要从外部访问wheel变量的值,我们需要使用该名称实现一个实例方法,该方法将在调用时返回该变量的值。这就是所谓的访问器方法。在一般的编程环境中,从对象外部访问实例变量的通常方法是实现访问器方法,也称为getter和setter方法。

在以下示例中,我们向Car类添加了getter和setter方法,以从对象外部访问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_reader而不是attr_accessor,它将是只读的。如果我们使用attr_writer,它将是只写的。这三种方法本身并不是获取器和设置器,而是在被调用时为我们创建获取器和设置器方法。它们是动态(以编程方式)生成其他方法的方法。这就是所谓的元编程。

第一个(较长)示例未使用Ruby的内置方法,仅当在getter和setter方法中需要附加代码时才应使用。例如,在将值分配给实例变量之前,setter方法可能需要验证数据或进行一些计算。

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


-2

嗯 很多好的答案。这是我的几分钱。

  • attr_accessor是一种简单的方法,可帮助我们清理(干燥重复的getter and setter方法。

  • 这样我们就可以将更多精力放在编写业务逻辑上,而不必担心设置者和获取者。


-3

attr_accessor相对于其他功能的主要功能是从其他文件访问数据的能力。
因此,通常您将拥有attr_reader或attr_writer,但好消息是Ruby使您可以将这两个与attr_accessor结合在一起。我认为它是我可以使用的方法,因为它更全面或更通用。另外,请记住,在Rails中,消除了这一点,因为它在后端为您做到了。因此,换句话说:与其他两个相比,最好使用attr_acessor,因为您不必担心具体化,访问器将覆盖所有内容。我知道这更多是笼统的解释,但对初学者来说有帮助。

希望这对您有所帮助!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.