Ruby Hash初始化混乱

时间:2015-09-04 08:37:39

标签: ruby oop hash initialization-list

我有两节课。

class Sky
  attr_accessor :args
  def initialize(args)
    @args = args
    puts 'Initializing sky'
  end
end


class ShadowMask
  attr_accessor :sky

  def initialize(args)
    args.each{|k, v| p "#{k}: #{v.to_s}"}
    @sky = args.fetch(:sky, Sky.new({}))
  end
end
可以使用默认ShadowMask

创建

Sky

sm_default = ShadowMask.new({})
# Initializing sky
# => #<ShadowMask:0x007fa215230eb0 @sky=#<Sky:0x007fa215230e60 @args={}>>
sm_default.sky
# => #<Sky:0x007fa215230e60 @args={}>
sm_default.sky.args
# => {}

或之前已创建的Sky

skyobj = Sky.new("Sky Object")
# Initializing sky
# => #<Sky:0x007fa21481a020 @args="Sky Object">
sm = ShadowMask.new(:sky => skyobj)
# "sky: #<Sky:0x007fa21481a020>"
# Initializing sky
# => #<ShadowMask:0x007fa21521ae80 @sky=#<Sky:0x007fa21481a020 @args="Sky Object">>

在第二种情况下,Sky的实例已经存在,我不希望从Initializing sky初始化中看到输出Sky

我的实际代码存在的问题是

puts 'Initializing sky'

是对方法的调用,该方法执行多次计算以完成初始化并设置多个属性。每次创建ShadowMask时都不需要重复此操作。

有趣的是,如果我替换

@sky = args.fetch(:sky, Sky.new({}))

类似

@sky = args.fetch(:sky, 'AnyString')

它工作正常,但如果需要,我将无法创建新的Sky

我不确定问题是在语法中还是我犯了一个概念错误。

2 个答案:

答案 0 :(得分:4)

我认为你需要将阻止传递给fetch才能看到Initializing sky

@sky = args.fetch(:sky) {Sky.new({})}

这背后的想法是,当你调用任何方法时,最初它的params(在这种情况下是Sky.new({}))将被调用。当您传递阻止时 - 它将在方法fetch内部之后调用,而不是之前。

答案 1 :(得分:2)

如果您想要的是在省略键Sky时提供默认的:sky对象,那么fetch是次优选择。这会更好:

@sky = args[:sky] || Sky.new({})

&#34;问题&#34;使用fetch,这将导致nil sm.sky

sm = ShadowMask.new(sky: nil)

如果这是您想要的行为,请使用fetch。如果没有,请使用||