什么是Ruby中的attr_accessor?

我很难理解ruby中的attr_accessor
谁能给我解释一下?

523203 次浏览

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

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

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

attr_accessor和朋友的需求是,好:

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

  2. 样板烂透了

希望这能澄清一些小细节。编码愉快。


1这不是严格正确的,有围绕这个的一些“技巧”,但没有对“公共实例变量”访问的语法支持。

attr_accessor非常简单:

attr_accessor :foo

是一个快捷方式:

def foo=(val)@foo = valend
def foo@fooend

它只不过是一个对象的getter/setter

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

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

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

所以这里的代码:

class Fooattr_accessor :barend

等效于此代码:

class Foodef bar@barenddef bar=( new_value )@bar = new_valueendend

您可以在Ruby中自己编写这种方法:

class Moduledef var( method_name )inst_variable_name = "@#{method_name}".to_symdefine_method method_name doinstance_variable_get inst_variable_nameenddefine_method "#{method_name}=" do |new_value|instance_variable_set inst_variable_name, new_valueendendend
class Foovar :barend
f = Foo.newp f.bar     #=> nilf.bar = 42p f.bar     #=> 42

假设你有一个类Person

class Personend
person = Person.newperson.name # => no method error

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

class Persondef name@name # simply returning an instance variable @nameendend
person = Person.newperson.name # => nilperson.name = "Dennis" # => no method error

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

class Persondef name@nameend
def name=(str)@name = strendend
person = Person.newperson.name = 'Dennis'person.name # => "Dennis"

太棒了。现在我们可以使用读取器和写入器方法写入和读取实例变量@name。除了,这很频繁,为什么每次都浪费时间编写这些方法?我们可以做得更容易。

class Personattr_reader :nameattr_writer :nameend

即使这样也会变得重复。当你同时需要读者和作家时,只需使用访问器!

class Personattr_accessor :nameend
person = Person.newperson.name = "Dennis"person.name # => "Dennis"

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

class Personattr_accessor :name
def greeting"Hello #{@name}"endend
person = Person.newperson.name = "Dennis"person.greeting # => "Hello Dennis"

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

简单地attr-accessor为指定的属性创建gettersetter方法

我认为混淆新Rubyist/程序员(如我自己)的部分原因是:

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

有点笼统,但这就是它对我的影响:

给定:

class Personend

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

因此,如果我们:

baby = Person.new

…试着给他们起个名字…

baby.name = "Ruth"

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

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

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

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

注意

attr_reader :v is equivalant todef v@vend
attr_writer :v is equivalant todef v=(value)@v=valueend

所以

attr_accessor :v which meansattr_reader :v; attr_writer :v

等价于为类定义一个setter和getter。

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

一般方式中的Getter和Setter

class Persondef name@nameend
def name=(str)@name = strendend
person = Person.newperson.name = 'Eshaan'person.name # => "Eshaan"

Setter方法

def name=(val)@name = valend

Getter方法

def name@nameend

Ruby中的Getter和Setter方法

class Personattr_accessor :nameend
person = Person.newperson.name = "Eshaan"person.name # => "Eshaan"

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

module Modattr_accessor(:one, :two)endMod.instance_methods.sort   #=> [:one, :one=, :two, :two=]

我也遇到了这个问题,并对这个问题写了一个有点长的答案。关于这个问题已经有了一些很好的答案,但是任何想要更多澄清的人,我希望我的回答能有所帮助

初始化方法

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

class Person
def initialize(name)@name = nameend

def greeting"Hello #{@name}"endend
person = Person.new("Denis")puts person.greeting

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

class Personattr_accessor :name
# def initialize(name)#     @name = name# end
def greeting"Hello #{name}"endend
person = Person.newperson.name = "Dennis"puts person.greeting

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

这两种“方法”都可以完成这项工作,但初始化可以节省我们的时间和代码行。

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

Getters, Setters(attr_reader,attr_writer,attr_accessor)

getter,attr_reader:getter的全部目的是返回特定实例变量的值。请访问下面的示例代码以获取有关此的详细信息。

class Item
def initialize(item_name, quantity)@item_name = item_name@quantity = quantityend
def item_name@item_nameend
def quantity@quantityendend
example = Item.new("TV",2)puts example.item_nameputs example.quantity

在上面的代码中,您在Item“example”的实例上调用了方法“item_name”和“数量”。“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 = quantityend
end
item = Item.new("TV",2)puts item.item_nameputs item.quantity

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

Setters,attr_writer:一开始使用setter方法让我感到困惑的是,在我看来,它似乎执行与初始化方法相同的功能。下面我根据我的理解解释差异;

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

但是如果你想在创建实例之后设置这些值,或者在实例初始化后更改它们呢?这将是你将使用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_nameattr_writer :item_name
end
item = Item.newputs item.item_name = "TV"

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

class Item
attr_reader :item_name
def initialize(item_name)@item_name = item_nameend
end
item = Item.new("TV")puts item.item_name

attr_accessor:执行attr_reader和attr_writer的功能,为您节省更多代码。

简单的解释,没有任何代码

上述大多数答案都使用代码。本解释试图通过类比/故事在不使用任何代码的情况下回答它:

外界不能接触中情局内部机密

  • 让我们想象一个非常秘密的地方:中情局。除了中情局内部的人之外,没有人知道中情局发生了什么。换句话说,外部人员无法获得中情局的任何信息。但是,因为拥有一个完全保密的组织是不好的,某些信息被提供给外部世界——当然,只有中情局希望每个人都知道的事情:例如中情局局长,这个部门与所有其他政府部门相比有多环保等等。其他信息:例如,谁是它在伊拉克或阿富汗的秘密特工——这些类型的事情可能在未来150年内仍然是秘密。

  • 如果你在CIA之外,你只能访问它向公众提供的信息。或者用CIA的说法,你只能访问被“清除”的信息。

  • 中情局希望向中情局以外的公众提供的信息被称为:“强”属性。

读写属性的含义:

  • 就CIA而言,大多数属性都是只读的。这意味着如果你是CIA的外部方,你可以问:谁是CIA的局长?你会得到一个直接的答案。但是你不能用只读属性做的是在CIA中做出改变。例如,你不能打电话,突然决定你希望金·卡戴珊成为局长,或者你希望帕丽斯·希尔顿成为总司令。

  • 如果属性赋予您“写入”权限,那么即使您在外部,您也可以根据需要进行更改。否则,您唯一能做的就是读取。

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

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

  • 另一方面,如果你已经是中情局的0号人物,那么你可以很容易地打电话给你在喀布尔的中情局特工,因为这些信息很容易获得,因为你已经在里面了。但如果你是中情局的1号人物,你根本不会被授予访问权限:你将无法知道他们是谁(读取权限),你将无法改变他们的任务(写入权限)。

类和你访问它们中的变量、属性和方法的能力完全相同。HTH!任何问题,请提问,我希望我能澄清。

与其他文件相比,attr_accessor的主要功能是从其他文件访问数据的能力。
因此,您通常会有attr_reader或attr_writer,但好消息是Ruby允许您将这两者与attr_accessor结合在一起。此外,请记住,在Rails中,这被消除了,因为它在后端为您执行。换句话说:您最好使用attr_acessor而不是其他两个,因为您不必担心过于具体,访问器涵盖了所有内容。我知道这更像是一个一般的解释,但它对我作为初学者有所帮助。

希望这有帮助!

属性和访问器方法

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

class Cardef initialize@wheels = 4  # This is an instance variableendend
c = Car.newc.wheels     # Output: NoMethodError: undefined method `wheels' for #<Car:0x00000000d43500>

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

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

class Cardef wheels  # getter method@wheelsend
def wheels=(val)  # setter method@wheels = valendend
f = Car.newf.wheels = 4  # The setter method was invokedf.wheels  # The getter method was invoked# Output: => 4

上面的示例工作,类似的代码通常用于创建其他语言中的getter和setter方法。然而,Ruby提供了一种更简单的方法来做到这一点:称为attr_reader、attr_writer和attr_acessor的三个内置方法。attr_reader方法使实例变量从外部可读,attr_writer使其可写,attr_acessor使其可读可写。

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

class Carattr_accessor :wheelsend
f = Car.newf.wheels = 4f.wheels  # Output: => 4

在上面的例子中,从对象外部可以读和写的属性。如果我们使用attr_reader而不是attr_accessor,它将是只读的。如果我们使用attr_writer,它将是只写的。这三个方法本身不是getter和setter,但是,当被调用时,它们为我们创建了getter和setter方法。它们是动态(以编程方式)生成其他方法的方法;这就是所谓的元编程。

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

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

理解它的另一种方法是通过attr_accessor来确定它消除了哪些错误代码。

示例:

class BankAccountdef initialize( account_owner )@owner = account_owner@balance = 0end
def deposit( amount )@balance = @balance + amountend
def withdraw( amount )@balance = @balance - amountendend

以下方法可用:

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

以下方法抛出错误:

$ bankie.owner     #undefined method `owner'...$ bankie.balance   #undefined method `balance'...

从技术上讲,ownerbalance不是一种方法,而是一个属性。银行账户类没有def ownerdef balance。如果有,那么你可以使用下面的两个命令。但是这两个方法不存在。但是,你可以访问属性,就像你通过attr_accessor访问方法一样!!#4这个词。属性。访问器。它像你访问方法一样访问属性。

添加attr_accessor :balance, :owner允许您读写balanceowner“方法”。现在您可以使用最后2种方法。

$ bankie.balance$ bankie.owner

总结一个属性访问器attr_accessor给你两个免费的方法。

就像在Java他们被称为getter和setter。

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

#the_attribute

#the_attribute=

在旧的ruby文档中,哈希标签#表示方法。它还可以包含类名前缀…我的班级#my_method

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

  • attr_accessor是一个简单的方法,可以帮助我们清理(DRY-ing重复的getter and setter方法。

  • 这样我们就可以更专注于编写业务逻辑,而不用担心setter和getter。

我是ruby的新手,不得不处理理解以下奇怪之处。将来可能会帮助其他人。最后,如上所述,其中2个函数(def myvar, def myvar=)都隐式访问@myvar,但这些方法可以被本地声明覆盖。

class Fooattr_accessor 'myvar'def initialize@myvar = "A"myvar = "B"puts @myvar # Aputs myvar # B - myvar declared above overrides myvar methodend
def testputs @myvar # Aputs myvar # A - coming from myvar accessor
myvar = "C" # local myvar overrides accessorputs @myvar # Aputs myvar # C
send "myvar=", "E" # not running "myvar =", but instead calls setter for @myvarputs @myvar # Eputs myvar # Cendend

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

  • 您可以动态修改模块/类

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

好的,想象如下:

class Nameabledef self.named(whatvalue)define_method :name do whatvalue endendend

我们正在声明一个类方法named,当使用值调用时,创建实例方法称为name,它返回该值。这是元编程部分。

现在我们将子类化该类:

class Dog < Nameablenamed "Fido"end

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

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

def name"Fido"end

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

puts Dog.new.name #=> Fido

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