Ruby - 关键字参数 - 您能将所有关键字参数视为哈希吗?怎么样?

Jes*_*mer 30 ruby hash arguments options keyword

我有一个看起来像这样的方法:

def method(:name => nil, :color => nil, shoe_size => nil) 
  SomeOtherObject.some_other_method(THE HASH THAT THOSE KEYWORD ARGUMENTS WOULD MAKE)
end
Run Code Online (Sandbox Code Playgroud)

对于任何给定的调用,我可以接受任意值的任意组合.我喜欢命名参数,因为我可以查看方法的签名以查看可用的选项.

我不知道的是,如果上面的代码示例中有大写字母描述的快捷方式.

回到过去,它曾经是:

def method(opts)
  SomeOtherObject.some_other_method(opts)
end
Run Code Online (Sandbox Code Playgroud)

优雅,简单,几乎作弊.

这些关键字参数是否有快捷方式,还是我必须在方法调用中重构我的选项哈希?

Den*_*nis 19

是的,这可能的,但它不是很优雅.

您将不得不使用parameters方法,该方法返回方法的参数及其类型的数组(在这种情况下,我们只有关键字参数).

def foo(one: 1, two: 2, three: 3)
  method(__method__).parameters
end  
#=> [[:key, :one], [:key, :two], [:key, :three]]
Run Code Online (Sandbox Code Playgroud)

知道了,有多种方法可以使用该数组来获取所有参数及其提供值的哈希值.

def foo(one: 1, two: 2, three: 3)
  params = method(__method__).parameters.map(&:last)
  opts = params.map { |p| [p, eval(p.to_s)] }.to_h
end
#=> {:one=>1, :two=>2, :three=>3}
Run Code Online (Sandbox Code Playgroud)

所以你的例子看起来像

def method(name: nil, color: nil, shoe_size: nil)
  opts = method(__method__).parameters.map(&:last).map { |p| [p, eval(p.to_s)] }.to_h
  SomeOtherObject.some_other_method(opts)
end
Run Code Online (Sandbox Code Playgroud)

仔细考虑使用它.它很聪明但是以可读性为代价,其他人阅读你的代码却不喜欢它.

您可以使用辅助方法使其更具可读性.

def params # Returns the parameters of the caller method.
  caller_method = caller_locations(length=1).first.label  
  method(caller_method).parameters 
end

def method(name: nil, color: nil, shoe_size: nil)
  opts = params.map { |p| [p, eval(p.to_s)] }.to_h
  SomeOtherObject.some_other_method(opts)
end
Run Code Online (Sandbox Code Playgroud)

更新:引入了Ruby 2.2 Binding#local_variables,可以代替使用Method#parameters.要小心,因为local_variables在方法中定义任何其他局部变量之前必须调用.

# Using Method#parameters
def foo(one: 1, two: 2, three: 3)
  params = method(__method__).parameters.map(&:last)
  opts = params.map { |p| [p, eval(p.to_s)] }.to_h
end
#=> {:one=>1, :two=>2, :three=>3}

# Using Binding#local_variables (Ruby 2.2+)
def bar(one: 1, two: 2, three: 3)
  binding.local_variables.params.map { |p|
    [p, binding.local_variable_get(p)]
  }.to_h
end
#=> {:one=>1, :two=>2, :three=>3}
Run Code Online (Sandbox Code Playgroud)

  • @jmoon90 是的,我们不想做 `params = { ... }` 因为这样我们就对实现进行了硬编码,并且它变得非常耦合。像我的示例中那样这样做的好处是,您可以更改方法签名,并且仍然自动捕获所有关键字参数。我不确定我是否理解您关于“named_pa​​rams”的其他问题。 (2认同)
  • 使用`local_variables`时最好使用`binding.local_variable_get(p)`而不是`eval(p.to_s)`,只是为了避免那种邪恶的`eval`...... (2认同)

Mat*_*ira 8

当然!只需使用double splat(**)运算符即可.

def print_all(**keyword_arguments)
  puts keyword_arguments
end

def mixed_signature(some: 'option', **rest)
  puts some
  puts rest
end

print_all example: 'double splat (**)', arbitrary: 'keyword arguments'
# {:example=>"double splat (**)", :arbitrary=>"keyword arguments"}

mixed_signature another: 'option'
# option
# {:another=>"option"}
Run Code Online (Sandbox Code Playgroud)

它就像常规的splat(*)一样,用于收集参数.您甚至可以将关键字参数转发给另一个方法.

def forward_all(*arguments, **keyword_arguments, &block)
  SomeOtherObject.some_other_method *arguments,
                                    **keyword_arguments,
                                    &block
end
Run Code Online (Sandbox Code Playgroud)

  • 不,我有兴趣将所有可选的,命名的关键字参数收集到哈希中.我不是要创建一个新的选项哈希.我想要一个{:name => val,:color => val等}的哈希值,它们在方法签名中命名. (13认同)