使用访问器宏和自我来理解ruby类属性

时间:2012-03-21 17:33:25

标签: ruby

所以我在ruby中创建了一个类:

class User
  def initialize
  end
end

现在说我想创建一个带有getter / setter的哈希属性,我对我这样做的选项感到困惑。

如果我这样做:

class User
  attr_accessor :some_hash
end

但我不希望这个哈希值为零,总是空哈希。

我应该这样做时感到困惑:

def some_hash
  self.some_hash ||= {}
end

并且做:

def some_hash
  @some_hash ||= {}
end

有什么区别?

如果我不使用attr_accessor,我必须同时创建阅读器和编写器(或getter / setter):

def some_hash=()
end

def some_hash
end

我希望有人可以清理创建some_hash属性的选项,这是一个哈希值,如果它是空的话,它永远不会返回nil。

即。使用attr_accessor,手动创建方法,最后何时使用@some_hash和self.some_hash

2 个答案:

答案 0 :(得分:8)

attr_accessor :some_hash定义给定属性的reader和writer方法。它相当于:

class User
  def some_hash
    @some_hash
  end

  def some_hash=(some_hash)
    @some_hash = some_hash
  end
end

@some_hash是指对象的实例变量,而some_hashsome_hash=方法。前者返回变量的值,后者设置它。

成语self.some_hash ||= {}相当于self.some_hash || self.some_hash = {}

Ruby short circuit中的布尔运算符,这意味着如果第一个表达式(self.some_hash = {})返回一个真值,则根本不会执行第二个表达式(self.some_hash)。

方法:

def some_hash
  self.some_hash ||= {}
end

实际上是递归的,因为它扩展为some_hash || self.some_hash = {}。它将一直调用自己,直到你得到堆栈溢出。使用第二种形式:

def some_hash
  @some_hash ||= {}
end

由于它直接设置实例变量,因此您不会遇到递归问题,也不必调用writer方法。 some_hash永远无法返回nil,因为如果@some_hashnil,则会在方法返回之前为其分配一个空哈希值。

顺便说一下,这也称为lazy initialization,因为变量在首次访问时初始化,而不是在创建User实例时初始化。

答案 1 :(得分:4)

除了马修斯的答案之外,还有一些关于属性的内容:

属性是方法。它们定义了如何处理某些符号。 attr_reader定义了getter,attr_writer定义了setter,attr_accessor定义了两者。虽然属性只接受符号,但它不会“声明”任何实例变量,因为Ruby中没有声明变量的东西。它在第一次使用时始终被声明和初始化。

因此,在您说attr_accessor :some_hash之后,运行时将知道访问名为some_hash的变量的方法,但是在您使用它之前该变量不存在。你可以尝试:

class User
  attr_accessor :some_hash
  attr_reader :some_other_hash
end
usr = User.new
p usr.some_hash

usr.some_hash = {}
p usr.some_hash

p usr.some_other_hash
usr.some_other_hash = {}

您将获得nil,{},nil, and an error。该错误是因为只定义了getter方法但没有setter。如果您想自己定义一个setter,您仍然可以编写一个名为some_other_hash=()

的方法

我对实例变量的建议是(我和其他许多人使用的):

class User
  attr_accessor :some_hash

  def initialize
    @some_hash = {}
  end

end
usr = User.new
p usr.some_hash

这种方法在为类创建对象时执行“psudo”声明,并且实例变量也将被初始化。