什么是Ruby中的attr_accessor?

den*_*icz 990 ruby

attr_accessor在Ruby中很难理解.谁可以给我解释一下这个?

Max*_*yak 2292

假设你有一堂课Person.

class Person
end

person = Person.new
person.name # => no method error
Run Code Online (Sandbox Code Playgroud)

显然我们从未定义过方法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
Run Code Online (Sandbox Code Playgroud)

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

class Person
  def name
    @name
  end

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

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Run Code Online (Sandbox Code Playgroud)

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

class Person
  attr_reader :name
  attr_writer :name
end
Run Code Online (Sandbox Code Playgroud)

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

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Run Code Online (Sandbox Code Playgroud)

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

class Person
  attr_accessor :name

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

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
Run Code Online (Sandbox Code Playgroud)

而已.为了理解如何attr_reader,attr_writer以及attr_accessor方法为您实际生成方法,请阅读其他答案,书籍,ruby文档.

  • @WilliamSmith要回答你的问题,你需要了解`attr_accessor`是一个在当前类上调用的方法,而`:name`是你传递给该方法的参数.它不是一种特殊的语法,它是一种简单的方法调用.如果你给它`@name`变量,它就没有意义,因为@name将包含`nil`.所以这就像写'attr_accessor nil`.您没有传递它需要创建的变量,而是传递您希望调用变量的名称. (201认同)
  • 使用Rails 3年,从来都不知道这一点.耻辱 (52认同)
  • @hakunin - 谢谢你这个明确的答案.我缺少的是为什么Ruby语法为attr_*语句中的实例变量建议冒号':'?似乎使用在类中其他地方使用的相同"@"语法来引用变量会更直接. (43认同)
  • @hakunin - 这完全有道理.我今天才知道ruby实际上是'运行',因为它解析了一个文件,并且每个语句和表达式实际上都是对某个对象的方法调用.包括attr_accessor.很有帮助. (23认同)
  • @Buminda是的,但方法`name`和变量`@name`不是一回事.不要混淆他们.你的类中有实例变量`@name`,你定义`attr_reader:name`就能从外面读取它.没有`attr_reader`,没有简单的方法可以在你的课外访问`@name`. (5认同)
  • Getter和setter方法无法更好地被告知.干杯! (2认同)

小智 125

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

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

attr_accessor朋友的需求是:

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

  2. 锅炉很糟糕

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


1这不是严格正确的,并且围绕此一些"技术",但没有对"公共实例变量"访问的语法支持.


Phr*_*ogz 67

attr_accessor是(如@pst所述)只是一种方法.它的作用是为您创造更多方法.

所以这里的代码如下:

class Foo
  attr_accessor :bar
end
Run Code Online (Sandbox Code Playgroud)

相当于这段代码:

class Foo
  def bar
    @bar
  end
  def bar=( new_value )
    @bar = new_value
  end
end
Run Code Online (Sandbox Code Playgroud)

您可以在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
Run Code Online (Sandbox Code Playgroud)

  • @bowsersenior你可能是对的,但它确实帮助了我. (12认同)
  • 这是一个很好的例子,即使在最初级的场景中也可以使用元编程.非常好. (3认同)
  • 我一直在寻找`attr_accessor`的实现草图,终于在这里找到了!尽管它解决了我的问题,但我很好奇我在哪里可以找到这样的实现示例? (2认同)

efa*_*cao 37

attr_accessor 很简单:

attr_accessor :foo
Run Code Online (Sandbox Code Playgroud)

是一个快捷方式:

def foo=(val)
  @foo = val
end

def foo
  @foo
end
Run Code Online (Sandbox Code Playgroud)

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

  • 你的答案很好.根据我的字典,"快捷方式"意味着"更短的替代路线",而不是"语法糖"或"由翻译解释的宏". (10认同)

Tyl*_*ves 24

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

  • 虽然这个评论并不完全有用,但确实如此.突出显示在Ruby中的"get"方法之外不存在公共数据属性的事实,这对于试图学习该语言的人来说是非常有用的信息. (4认同)
  • 这真的不应该被低估.作为一个非Ruby人试图解决这个问题,这个答案非常有用! (3认同)

Chu*_*uck 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
Run Code Online (Sandbox Code Playgroud)


BKS*_*eon 14

没有任何代码的简单解释

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

外部各方无法访问内部中央情报局的秘密

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

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

  • 美国中央情报局希望向中央情报局以外的公众提供的信息称为:属性.

读写属性的含义:

  • 在CIA的情况下,大多数属性是"只读".这意味着如果你是中情局外部的一方,你可以问: "谁是中央情报局局长?" 你会得到一个直接的答案.但是,"只读"属性无法做到的是在CIA中进行更改.例如,你不能打电话,突然决定你想让金·卡戴珊成为导演,或者你希望帕丽斯·希尔顿成为总司令.

  • 如果属性为您提供"写入"访问权限,那么即使您在外面,也可以根据需要进行更改.否则,你唯一能做的就是阅读.

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

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

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

与类完全相同的事情以及访问其中的变量,属性和方法的能力.HTH!有任何问题,请询问,我希望我能澄清一下.


小智 13

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

一般方式的吸气剂和塞特犬

class Person
  def name
    @name
  end

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

person = Person.new
person.name = 'Eshaan'
person.name # => "Eshaan"
Run Code Online (Sandbox Code Playgroud)

二传法

def name=(val)
  @name = val
end
Run Code Online (Sandbox Code Playgroud)

吸气方法

def name
  @name
end
Run Code Online (Sandbox Code Playgroud)

Ruby中的Getter和Setter方法

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"
Run Code Online (Sandbox Code Playgroud)

  • 完美的解释!这是一个非常方便的行为,可以很容易地被覆盖. (2认同)

Jbu*_*r43 12

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

初始化方法

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

class Person

  def initialize(name)
    @name = name
  end


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

person = Person.new("Denis")
puts person.greeting
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我们使用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
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我们通过使用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
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,您在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
Run Code Online (Sandbox Code Playgroud)

这种语法的工作方式完全相同,只为它节省了六行代码.想象一下,如果你还有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
Run Code Online (Sandbox Code Playgroud)

下面的代码是使用attr_writer再次缩短代码并节省时间的示例.

class Item

attr_reader :item_name
attr_writer :item_name

end

item = Item.new
puts item.item_name = "TV"
Run Code Online (Sandbox Code Playgroud)

下面的代码重复上面的初始化示例,我们使用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
Run Code Online (Sandbox Code Playgroud)

attr_accessor:执行attr_reader和attr_writer的功能,为您节省一行代码.


Ben*_*Ben 10

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

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

更广泛一点,但这就是它为我点击的方式:

鉴于:

class Person
end
Run Code Online (Sandbox Code Playgroud)

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

那么如果我们那么:

baby = Person.new
Run Code Online (Sandbox Code Playgroud)

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

baby.name = "Ruth"
Run Code Online (Sandbox Code Playgroud)

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

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

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


Mar*_*ton 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
Run Code Online (Sandbox Code Playgroud)

所以

attr_accessor :v which means 
attr_reader :v; attr_writer :v 
Run Code Online (Sandbox Code Playgroud)

是等价的,为类定义一个setter和getter.


Vee*_*eru 5

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


Igg*_*ggy 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
Run Code Online (Sandbox Code Playgroud)

可以使用以下方法:

$ bankie = BankAccout.new("Iggy")
$ bankie 
$ bankie.deposit(100)
$ bankie.withdraw(5)
Run Code Online (Sandbox Code Playgroud)

以下方法引发错误:

$ bankie.owner     #undefined method `owner'... 
$ bankie.balance   #undefined method `balance'...
Run Code Online (Sandbox Code Playgroud)

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

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

$ bankie.balance
$ bankie.owner
Run Code Online (Sandbox Code Playgroud)