Ruby哈希初始化混乱



我有两个类。

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

可以使用默认Sky:创建ShadowMask

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

或者与先前创建的CCD_ 3:

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的实例已经存在,并且我不希望看到Sky初始化的输出Initializing sky

我实际代码的问题是

puts 'Initializing sky'

是对一个方法的调用,该方法执行多次计算以完成初始化并设置多个属性。并且在每次创建CCD_ 7时无需重复这一过程。

有趣的是,如果我更换

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

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

它运行良好,但如果需要,我会放弃创建新Sky的可能性。

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

我认为您需要将块传递给fetch才能看不到Initializing sky:

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

这背后的想法是,当您调用任何方法时,最初都会调用其params(在本例中为Sky.new({})(。当您传递块时,它将在方法fetch内部调用,而不是之前调用。

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

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

fetch的"问题"是,这将导致零sm.sky:

sm = ShadowMask.new(sky: nil)

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

最新更新