ChatGPT解决这个技术问题 Extra ChatGPT

Ruby 中的 attr_accessor 是什么?

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

attr_accessor 在 Git 中是否以同样的方式工作?我发现有些教程解释得不够充分,而另一些则假设有先验知识。
@Angelfirenze,gitattr_accessor 无关。 Git 是一个版本控制软件,而 attr_accessor 是一个 method in Ruby

D
Derrell Durrett

假设您有一个类 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"

惊人的。现在我们可以使用 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 文档。


@hakunin - 谢谢你的明确回答。我缺少的是为什么 Ruby 语法建议 attr_* 语句中的实例变量使用冒号“:”?似乎使用类中其他地方使用的相同的“@”语法来引用变量会更直接。
@WilliamSmith 要回答您的问题,您需要了解 attr_accessor 是在当前类上调用的方法,而 :name 是您传递给该方法的参数。这不是特殊的语法,它是一个简单的方法调用。如果你要给它 @name 变量,那就没有意义了,因为@name 将包含 nil。所以这就像写 attr_accessor nil。您没有传递它需要创建的变量,而是传递了您希望调用该变量的名称。
@hakunin - 这完全有道理。我今天刚刚了解到 ruby 在解析文件时实际上是在“运行”,并且每个语句和表达式实际上都是对某个对象的方法调用。包括 attr_accessor。非常有帮助。
使用 Rails 3 年,甚至不知道这一点。耻辱
@Buminda 是的,但是方法 name 和变量 @name 不是一回事。不要混淆他们。您的类中有实例变量 @name,并且您定义 attr_reader :name 以便能够从外部读取它。如果没有 attr_reader,您就无法在课堂外访问 @name
C
Community

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

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

attr_accessor 和朋友的需要是,嗯:

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

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

1 这并不完全正确,有 some "techniques" around this,但不支持“公共实例变量”访问的语法。


当您说 attr_accessor 是“只是一种方法”时,我明白了。但是用于调用该方法的语法是什么?我无法在 ruby 文档中找到有关诸如 some_method :name => "whatever", :notherName, :etc 之类的语法的部分
每当您使用“@”或“。”访问实例变量时,它似乎会自动调用。符号。
P
Phrogz

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

这是一个很好的例子,说明即使在最初级的场景中也可以使用元编程。非常好。
我一直在寻找 attr_accessor 的实现草图,终于在这里找到了!虽然它解决了我的问题,但我很想知道在哪里(书籍/官方文档)可以找到这样的实现示例?
e
efalcao

attr_accessor 非常简单:

attr_accessor :foo

是一个快捷方式:

def foo=(val)
  @foo = val
end

def foo
  @foo
end

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


你的回答很好。根据我的字典,“捷径”的意思是“一条较短的替代路线”,而不是“语法糖”或“解释器解释的宏”。
T
Tyler Eaves

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


虽然这条评论并不完全有用,但它是真的。强调公共数据属性不存在于 Ruby 中的“get”方法之外的事实,这对于尝试学习该语言的人来说非常有用。
这真的不应该被否决。作为一个试图弄清楚这些东西的非 Ruby 人,这个答案非常有帮助!
同意,似乎与 C# 的名称 {get;放;}
C
Chuck

它只是一个为实例变量定义 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

以这种方式处理多个属性很棒!
这是一个非常有用的代码片段,可以解决我与元编程相关的另一个问题。
C
CupawnTae

如果您熟悉 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"

完美的解释!这是一个非常方便的行为,可以很容易地被覆盖。
B
BenKoshy

简单解释,无需任何代码

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

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

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

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

CIA 想要向 CIA 以外的公众提供的信息称为:属性。

读写属性的含义:

在 CIA 的情况下,大多数属性是“只读的”。这意味着如果你是 CIA 以外的一方,你可以问:“谁是 CIA 的局长?”你会得到一个直接的答案。但是你不能用“只读”属性做的是在 CIA 中进行更改。例如,您不能在打完电话后突然决定要让金·卡戴珊担任导演,或者要让帕丽斯·希尔顿担任总司令。

如果属性为您提供“写入”访问权限,那么您可以根据需要进行更改,即使您在外面也是如此。否则,你唯一能做的就是阅读。换句话说,访问者允许您对不允许外部人员进入的组织进行查询或更改,具体取决于访问者是读取访问者还是写入访问者。

类内的对象可以很容易地相互访问

另一方面,如果您已经在中央情报局内部,那么您可以轻松地致电您在喀布尔的中央情报局特工,因为您已经在内部,因此很容易获得此信息。但是,如果您在 CIA 之外,您将无法获得访问权限:您将无法知道他们是谁(读取权限),并且您将无法更改他们的任务(写入权限)。

与类以及访问其中的变量、属性和方法的能力完全相同。 !任何问题,请询问,我希望我能澄清。


你的解释很有道理! +1 抱歉,您确定“CIA 清除的信息”的表述是正确的吗?
中央情报局有各种“许可”级别:例如最高机密(除了 Prez 之外没有人),或公众信任(每个人都可以阅读该信息)。 CIA 实际上提供了很多非常酷的事实!
您值得只为卡戴珊(Kardashian)的帕丽斯·希尔顿(Paris Hilton)的示例提供upvote :)我以为特朗普对总统来说已经足够糟糕了,想象一下那两个负责人的OMG!
是的!这就是我们需要的,没有代码的 StackOverflow! :-)
R
Rajkaran Mishra

我也遇到了这个问题,并为这个问题写了一个有点冗长的答案。已经有一些很好的答案,但是任何人都在寻找更多的澄清,我希望我的回答能有所帮助

初始化方法

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

class Person

  def initialize(name)
    @name = name
  end


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

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

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

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))。有关这些的更多详细信息,请参见下文。

获取器、设置器(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”将返回(或“get”)传递给“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"

下面的代码是对上面初始化示例的重复,我们在其中使用 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 的功能,为您节省多行代码。


B
Ben

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

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

更笼统一点,但这就是我点击的方式:

鉴于:

class Person
end

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

所以如果我们那么:

baby = Person.new

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

baby.name = "Ruth"

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

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

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


M
Marcus Thornton

简单地说,它将为该类定义一个 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。


V
Veeru

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


I
Iggy

另一种理解它的方法是找出它通过使用 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

P
Praveen_Shukla

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

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

D
Douglas G. Allen

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

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

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

#the_attribute

#the_attribute=

在旧的 ruby 文档中,井号标签 # 表示方法。它还可以包括类名前缀... MyClass#my_method


A
Adverbly

我是 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

B
BrunoF

属性和访问器方法

属性是可以从对象外部访问的类组件。它们在许多其他编程语言中被称为属性。它们的值可以通过使用“点符号”来访问,如 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 类的一个实例(对象)。我们尝试从对象外部读取wheels 实例变量的值,但没有成功。发生的事情是 Ruby 试图在 c 对象中调用一个名为 wheel 的方法,但没有定义这样的方法。简而言之,object_name.attribute_name 尝试在对象中调用名为 attribute_name 的方法。要从外部访问wheels 变量的值,我们需要使用该名称实现一个实例方法,该方法将在调用时返回该变量的值。这称为访问器方法。在一般编程上下文中,从对象外部访问实例变量的常用方法是实现访问器方法,也称为 getter 和 setter 方法。 getter 允许从外部读取类中定义的变量的值,而 setter 允许从外部写入变量的值。

在下面的示例中,我们向 Car 类添加了 getter 和 setter 方法,以从对象外部访问wheels 变量。这不是定义 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_accessor 的内置方法。 attr_reader 方法使实例变量从外部可读,attr_writer 使其可写,attr_accessor 使其可读可写。

上面的例子可以这样改写。

class Car
  attr_accessor :wheels
end

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

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

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

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


m
matt

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

您可以即时修改模块/类

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

好吧,想象一下:

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

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


S
Sergio Tulentsev

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

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

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


c
creativegeek

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

希望这有帮助!