在 Ruby (2.1+) 中调用超类初始值设定项时避免重复命名参数默认值
Avoid repeating named argument defaults when calling superclass initializer in Ruby (2.1+)
假设我有一个父 class,其初始化器有一个带有默认值的参数:
class Parent
attr_reader :foo
def initialize(foo: 123)
@foo = foo
end
end
我想创建一个与 foo
具有相同默认值的子 class。如果我重复声明,我可以这样做:
class Child < Parent
attr_reader :bar
def initialize(foo: 123, bar: 456)
super(foo: foo)
@bar = bar
end
end
然而,这意味着我必须写两次123
。如果我试图通过省略它来避免重复它 --
class Child < Parent
attr_reader :bar
def initialize(foo:, bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 这意味着以前可选的、默认的 foo
现在 需要 由 subclass,我实际上没有得到默认值的任何好处。
我想我可以在子class中将它默认为nil
--
class Child < Parent
attr_reader :bar
def initialize(foo: nil, bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 但没有; Child.new(bar:789).foo
现在是 nil
,而我想要的是 123
。
我也不能完全忽略这个论点 --
class Child < Parent
attr_reader :bar
def initialize(bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 因为如果我尝试指定它 (Child.new(foo: 345, bar:789)
) 我会得到 unknown keyword: foo (ArgumentError)
.
有什么方法可以 保留一个参数 ,而不是给它一个默认值? And/or 一种允许初始化器获取任意附加命名参数并将它们传递给其超级class 初始化器的方法?
更新: 我想出了以下技巧(基本上是我自己动手 'default parameters'),但我对此不是很满意。
class Parent
attr_reader :foo
def initialize(foo: nil)
@foo = foo || 123 # faking 'default'-ness
end
end
class Child < Parent
attr_reader :bar
def initialize(foo: nil, bar: 456)
super(foo: foo)
@bar = bar
end
end
肯定还有更多 Ruby-ish 方法可以做到这一点?
这更像是 Ruby-1.9-ish 方式而不是 Ruby-2.1-ish 方式,但我相信它可以满足您的需求,同时避免重复:
class Parent
attr_reader :foo
def initialize(opts = {})
@foo = opts[:foo] || 123
end
end
class Child < Parent
attr_reader :bar
def initialize(opts = {})
super(opts)
@bar = opts[:bar] || 456
end
end
puts Parent.new.foo # => 123
puts Parent.new(foo: 1).foo # => 1
puts Parent.new(bar: 2).foo # => 123
puts Parent.new(foo: 1, bar: 2).foo # => 1
在Ruby 2.0+中,您可以使用双splat运算符。
def initialize(bar: 456, **args)
super(**args)
@bar = bar
end
一个例子:
[1] pry(main)> class Parent
[1] pry(main)* def initialize(a: 456)
[1] pry(main)* @a = a
[1] pry(main)* end
[1] pry(main)* end
=> :initialize
[2] pry(main)> class Child < Parent
[2] pry(main)* def initialize(b: 789, **args)
[2] pry(main)* super(**args)
[2] pry(main)* @b = b
[2] pry(main)* end
[2] pry(main)* end
=> :initialize
[3] pry(main)> ch = Child.new(b: 3)
=> #<Child:0x007fc00513b128 @a=456, @b=3>
[4] pry(main)> ch = Child.new(b: 3, a: 6829)
=> #<Child:0x007fc00524a550 @a=6829, @b=3>
双 splat 运算符类似于单 splat 运算符,但它不是将所有额外的 args 捕获到数组中,而是将它们捕获到哈希中。然后,当用作 super 的参数时,double splat 将散列扁平化为命名参数,有点像 single splat 对数组的作用。
假设我有一个父 class,其初始化器有一个带有默认值的参数:
class Parent
attr_reader :foo
def initialize(foo: 123)
@foo = foo
end
end
我想创建一个与 foo
具有相同默认值的子 class。如果我重复声明,我可以这样做:
class Child < Parent
attr_reader :bar
def initialize(foo: 123, bar: 456)
super(foo: foo)
@bar = bar
end
end
然而,这意味着我必须写两次123
。如果我试图通过省略它来避免重复它 --
class Child < Parent
attr_reader :bar
def initialize(foo:, bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 这意味着以前可选的、默认的 foo
现在 需要 由 subclass,我实际上没有得到默认值的任何好处。
我想我可以在子class中将它默认为nil
--
class Child < Parent
attr_reader :bar
def initialize(foo: nil, bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 但没有; Child.new(bar:789).foo
现在是 nil
,而我想要的是 123
。
我也不能完全忽略这个论点 --
class Child < Parent
attr_reader :bar
def initialize(bar: 456)
super(foo: foo)
@bar = bar
end
end
-- 因为如果我尝试指定它 (Child.new(foo: 345, bar:789)
) 我会得到 unknown keyword: foo (ArgumentError)
.
有什么方法可以 保留一个参数 ,而不是给它一个默认值? And/or 一种允许初始化器获取任意附加命名参数并将它们传递给其超级class 初始化器的方法?
更新: 我想出了以下技巧(基本上是我自己动手 'default parameters'),但我对此不是很满意。
class Parent
attr_reader :foo
def initialize(foo: nil)
@foo = foo || 123 # faking 'default'-ness
end
end
class Child < Parent
attr_reader :bar
def initialize(foo: nil, bar: 456)
super(foo: foo)
@bar = bar
end
end
肯定还有更多 Ruby-ish 方法可以做到这一点?
这更像是 Ruby-1.9-ish 方式而不是 Ruby-2.1-ish 方式,但我相信它可以满足您的需求,同时避免重复:
class Parent
attr_reader :foo
def initialize(opts = {})
@foo = opts[:foo] || 123
end
end
class Child < Parent
attr_reader :bar
def initialize(opts = {})
super(opts)
@bar = opts[:bar] || 456
end
end
puts Parent.new.foo # => 123
puts Parent.new(foo: 1).foo # => 1
puts Parent.new(bar: 2).foo # => 123
puts Parent.new(foo: 1, bar: 2).foo # => 1
在Ruby 2.0+中,您可以使用双splat运算符。
def initialize(bar: 456, **args)
super(**args)
@bar = bar
end
一个例子:
[1] pry(main)> class Parent
[1] pry(main)* def initialize(a: 456)
[1] pry(main)* @a = a
[1] pry(main)* end
[1] pry(main)* end
=> :initialize
[2] pry(main)> class Child < Parent
[2] pry(main)* def initialize(b: 789, **args)
[2] pry(main)* super(**args)
[2] pry(main)* @b = b
[2] pry(main)* end
[2] pry(main)* end
=> :initialize
[3] pry(main)> ch = Child.new(b: 3)
=> #<Child:0x007fc00513b128 @a=456, @b=3>
[4] pry(main)> ch = Child.new(b: 3, a: 6829)
=> #<Child:0x007fc00524a550 @a=6829, @b=3>
双 splat 运算符类似于单 splat 运算符,但它不是将所有额外的 args 捕获到数组中,而是将它们捕获到哈希中。然后,当用作 super 的参数时,double splat 将散列扁平化为命名参数,有点像 single splat 对数组的作用。