【问题标题】:Intermingling attr_accessor and an initialize method in one class在一个类中混合 attr_accessor 和初始化方法
【发布时间】:2013-05-20 18:28:59
【问题描述】:

我看到如下代码:

class Person
  def initialize(name)
    @name = name
  end
end

我知道这让我可以做person = Person.new 之类的事情,并可以像其他方法一样在课堂上的其他地方使用@name。然后,我看到如下代码:

class Person
  attr_accessor :name
end

...

person = Person.new
person.name = "David"

我对这两种网格方法感到不知所措。 def initialize(name) 的特殊用途是什么?我想attr_accessor 允许我读写。这意味着它们是两种不同的方法。是的?想要了解def initializeattr_accessor 以及它们的啮合方式。

【问题讨论】:

    标签: ruby initialization accessor


    【解决方案1】:

    initializeattr_accessor 彼此无关。 attr_accessor :name 创建了几个方法:

    def name
      @name
    end
    
    def name=(val)
      @name = val
    end
    

    如果要在创建对象时设置名称,可以在初始化程序中进行:

    def initialize(name)
      @name = name
      # or
      # self.name = name
    end
    

    但您不必这样做。您可以在创建后设置名称。

    p = Person.new
    p.name = "David"
    puts p.name # >> "David"
    

    【讨论】:

    • 我明白了。初始化方法被用作创建对象时初始化值的一种方式。这不是必需的,但是如果对象有很多属性,那么能够一次完成所有操作而不是在创建之后花时间设置所有属性是有意义的。是吗?
    • @David 有点像。需要注意的是,如果对象有很多属性,初始化器很快就会变成一堆无用的有序参数。这就是为什么 (a) 我们现在已经命名了参数,和/或 (b) 您可以使用映射来提高构造点的可读性。
    • @DaveNewton 你有一个非常简短的例子吗?我想我知道你的意思,但想确定一下。当你说我们现在有命名参数时,你是否暗示我们过去没有?在定义方法时,我们怎么会设法说“我想接受这个和那个参数”?
    • @David“命名参数”指的是在 调用 端可用的名称,而不是在方法声明端,显然它们在 Ruby 中具有名称,并且始终具有 AFAIK。对于真正粗略的概述,请参阅this gist
    • 我明白了。非常感谢。
    【解决方案2】:

    这是您正在寻找的答案Classes and methods。请仔细阅读。

    这是来自链接的一个很好的文档:

    类和方法

    现在我们准备好创建我们自己的地址类。让我们从简单的开始。让我们从只包含“street”字段的地址开始。

    这是定义类的方式:

    class Address
        def initialize(street)  
            @street = street 
        end
    end
                       
    

    让我们来看看这个:

    • class关键字定义了一个类。

    • 通过在这个类中定义一个方法,我们将它与这个类关联起来。

    • initialize 方法是实际构造数据结构的方法。每个类都必须包含一个初始化方法。

    @street 是一个对象变量。类似于哈希的键。 @ 符号将@street 区分为对象变量。每次创建 Address 类的对象时,该对象都会包含一个 @street 变量。

    让我们使用这个类来创建一个地址对象。

    address = Addres.new("23 St George St.")  
                       
    

    就是这样。地址现在是类地址的一个对象 读取对象中的数据

    假设我们要读取地址对象中的数据。为此,我们需要编写一个返回此数据的方法:

    class Address
        def initialize(street)   
            @street = street 
        end
    
        # Just return @street
        def street
            @street
        end
    end
                       
    

    现在,Address#street 方法可以让您读取地址所在的街道。在 irb 中:

    >> address.street 
    => "23 St George St."
                       
    

    在外部可见的对象的属性称为属性。在这种情况下,街道是一个属性。特别是,它是一个可读属性。因为这种属性很常见,Ruby 通过 attr_reader 关键字为您提供了一个快捷方式:

    class Address
        attr_reader :street
        def initialize(street)   
            @street = street 
        end
    end
    
                       
    

    更改对象中的数据

    我们还可以定义一个方法来更改对象中的数据。

    class Address
        attr_reader :street
        def initialize(street)  
            @street = street 
        end
        def street=(street)
            @street = street
        end
    end
    
                       
    

    Ruby 非常聪明地使用了 street= 方法:

    address.street = "45 Main St."
                       
    

    请注意,您可以将空格 betten street 和 =。现在我们可以更改地址数据,我们可以简化初始化方法,并将街道默认为空字符串“”。

    class Address
        attr_reader :street
        def initialize
            @street = ""
        end
        def street=(street)
            @street = street  
        end
    end
    
    address = Address.new
    address.street = "23 St George St."
    
    
                       
    

    这可能看起来不是很简单,但是当我们添加城市、州和邮政编码字段以及更多方法时,这将使类定义更加简单。

    现在,street 也是一个可写属性。和以前一样,您可以使用 attr_writer 声明它:

    class Address
        attr_reader :street
        attr_writer :street  
        def initialize
            @street = ""
        end
    end
                       
    

    访问数据

    很多时候,你的属性既是可读的又是可写的。 Ruby 允许您将这些与 attr_accessor 混为​​一谈。我猜这些会被称为“可访问属性”,但我从未见过它们被这样称呼。

    class Address
        attr_accessor :street  
        def initialize
            @street = ""
        end
    end
    
                       
    

    有了这些知识,现在很容易定义整个地址簿结构。事实证明,attr_accessor 和朋友都接受多个参数。

    class Address
        attr_accessor :street, :city, :state, :zip  
        def initialize
            @street = @city = @state = @zip = ""
        end
    end
                       
    

    【讨论】:

    • 请在答案中包含实际内容,而不仅仅是提供链接。仅链接的答案不受欢迎,因为 (a) 答案不能独立存在,以及 (b) 如果链接断开,那比无用更糟糕。
    • @DaveNewton 我完成了。我还有什么要补充的吗?请发表评论。
    • IMO 答案太长了,但这只是我的问题 - 它不是仅链接,这是我关心的问题。
    • @Priti 我注意到在最后一个代码 sn-p 中,我们有 def initialize; @street = @city = @state = @zip = "" 基本上你在这段代码 sn-p 中所做的是当我们初始化一个地址时,它将什么都没有,我必须写不同的属性,是吗?我试图确认address = Address.new("street", "city", "state", "zip") 无效,因为它们不在initialize 方法的参数列表中
    • @David 我发布的答案都是关于initializegettersetter 方法。仔细阅读你会得到你要找的答案,这两个很简单。
    【解决方案3】:

    我认为您将initialize 视为构造函数。准确地说,事实并非如此。默认构造函数是类上的new 方法,initialize 由该方法调用。如果你没有定义initialize,你仍然可以使用new 创建一个对象,因为initialize 不是构造函数本身。在这种情况下,默认的initialize 什么都不做。如果您确实定义了initialize,那么它会在对象创建后立即调用。

    @foo = ...attr_accessor :foo 的声明是不同的。前者为实例变量@foo 赋值,而后者允许您通过方法foofoo= 访问@foo。没有后者,你仍然可以通过直接描述so来访问@foo

    【讨论】:

    • 将 ''attr_accessor'' 视为构造函数是否正确,就像我们认为 initialize 是一个构造函数一样(行为类似但不是)?那就是实际的构造函数new 也调用attr_accessor,然后创建getter 和setter 还是发生其他事情?
    【解决方案4】:

    与 C++ 不同,Ruby 中的 Java 实例变量默认是 private(部分是因为它们可以通过使用 a.instance_variable_get :@x 访问)

    例如:

      class Dda
        def initialize task
            @task = task
            @done = false
        end
      end
    item = Dda.new "Jogging" # This would call the initializer and task = Jogging would
    be set for item
    item.task # would give error as their is no function named task to access the instance
    variable.
    

    虽然我们已将值设置为 item,但我们无法对它做任何事情,因为实例变量在 ruby​​ 中是私有的。 getter 代码:

    def task
      @task
    end
    #for getter
    def task=task
       @task = task
    end
    

    使用 getter 可以确保 item.task 返回它的值 并且使用 setter 让我们可以灵活地随时为实例变量提供值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-28
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 2021-06-12
      相关资源
      最近更新 更多