我很难理解Ruby中的attr_accessor
。有人可以向我解释一下吗?
答案 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_reader
,attr_writer
和attr_accessor
方法如何为您实际生成方法,请阅读其他答案,书籍,ruby文档。
答案 1 :(得分:121)
attr_accessor 只是一种方法。 (该链接应提供有关其工作原理的更多信息 - 查看生成的方法对,教程应向您展示如何使用它。)
诀窍是class
不是Ruby中的定义(它只是C ++和Java等语言中的“定义”),但它是一个表达式求值。在此评估期间,调用attr_accessor
方法,然后修改当前类 - 记住隐式接收器:self.attr_accessor
,其中self
是此时的“开放”类对象
attr_accessor
和朋友的需求是:
Ruby与Smalltalk一样,不允许在该对象的 1 方法之外访问实例变量。也就是说,无法以x.y
形式访问实例变量,例如Java或甚至Python。在Ruby中y
始终被视为要发送的消息(或“调用方法”)。因此,attr_*
方法创建包装器,通过动态创建的方法代理实例@variable
访问。
Boilerplate糟透了
希望这能澄清一些细节。快乐的编码。
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中进行更改。例如你不能打个电话突然决定你希望金·卡戴珊成为导演,或者你希望帕丽斯·希尔顿成为总司令。
如果属性给你"写"如果您愿意,即使您在外面也可以进行访问。否则,你唯一能做的就是阅读。
换句话说,访问者允许您对不允许外部人员进入的组织进行查询或进行更改,具体取决于访问者是读取还是写入访问者。
班级内的对象可以轻松互相访问
与类完全相同,并且能够访问其中的变量,属性和方法。 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
为指定的属性创建getter
和setter
方法
答案 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'...
从技术上讲, owner
和balance
不是方法,而是属性。 BankAccount类没有def owner
和def balance
。如果是,则可以使用以下两个命令。但这两种方法都不存在。但是,您可以访问属性,就像您通过attr_accessor
访问方法一样! 因此单词attr_accessor
。属性。访问。它访问属性,就像访问方法一样。
添加attr_accessor :balance, :owner
可让您读写balance
和owner
"方法"。现在你可以使用最后两种方法了。
$ 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而不是其他两个,因为你不必担心具体,访问者会覆盖它。我知道这更多是一般性的解释,但它帮助我作为初学者。
希望这有帮助!