具有可选的第一个哈希参数和 keyword_args 的奇怪方法行为
Odd method behaviour with optional first hash parameter and keyword_args
我有以下方法:
def test(first_param = nil, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
以下所有调用都按照我的预期进行:
test(:something)
#=> first_param: something
# keyword_arg:
test(nil, keyword_arg: :keyword_arg)
#=> first_param:
# keyword_arg: keyword_arg
test({ first_param: :is_a_hash }, keyword_arg: :is_still_working)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg: is_still_working
但是省略可选的 keyword_arg
并将散列作为第一个参数传递给我一个错误:
test(first_param: :is_a_hash)
#=> test.rb:1:in `test': unknown keyword: first_param (ArgumentError)
# from test.rb:12:in `<main>'
我希望它将 first_param
设置为 { first_param: :is_hash }
并且 keyword_arg
为 nil
。
它似乎将每个散列解释为关键字 arg:
test(keyword_arg: :should_be_first_param)
#=> first_param:
# keyword_arg: should_be_first_param
我认为这应该将 first_param
设置为 { keyword_arg: :should_be_first_param }
,留下 keyword_arg
nil
。
这是解析器错误还是预期行为?在 ruby 2.3.0 和 2.2.4.
上测试
编辑:将第一个参数设置为强制性,一切都像我期望的那样:
def test_mandatory(first_param, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
test_mandatory(first_param: :is_a_hash)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg:
test_mandatory(keyword_arg: :should_be_first_param)
#=> first_param: {:keyword_arg=>:should_be_first_param}
# keyword_arg:
我希望将参数设为可选不会改变参数的解析方式。
我打开了一个 issue on bugs.ruby-lang.org,然后开发人员可以弄清楚它是这样设计的还是 kword args 的副作用。
根据Marc-Andre Lafortune's reply预计:
This behavior may be surprising but it is intentional.
It boils down to giving priority to filling keyword arguments first
instead of filling unnamed parameters. It is actually the only
possible way to go. Among other things, think about the following
example:
def foo(*rest, bar: 42)
end
If we don't prioritize named arguments first, then there is simply no
way to specify a value for bar in this example!
So Ruby checks that:
- after all mandatory unnamed arguments are filled
- if the last remaining argument is hash-like
- and all its keys are symbols
- and the method called uses keyword arguments
=> then that parameter is used for keyword arguments.
Note the requirement on keys being symbols. This can yield even more
surprising if you pass a hash with some keys that are not symbols:
def foo(a = nil, b: nil)
p a, b
end
foo(:b => 42) # => nil, 42
foo('b' => 42) # => {"b" => 42}, nil
我有以下方法:
def test(first_param = nil, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
以下所有调用都按照我的预期进行:
test(:something)
#=> first_param: something
# keyword_arg:
test(nil, keyword_arg: :keyword_arg)
#=> first_param:
# keyword_arg: keyword_arg
test({ first_param: :is_a_hash }, keyword_arg: :is_still_working)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg: is_still_working
但是省略可选的 keyword_arg
并将散列作为第一个参数传递给我一个错误:
test(first_param: :is_a_hash)
#=> test.rb:1:in `test': unknown keyword: first_param (ArgumentError)
# from test.rb:12:in `<main>'
我希望它将 first_param
设置为 { first_param: :is_hash }
并且 keyword_arg
为 nil
。
它似乎将每个散列解释为关键字 arg:
test(keyword_arg: :should_be_first_param)
#=> first_param:
# keyword_arg: should_be_first_param
我认为这应该将 first_param
设置为 { keyword_arg: :should_be_first_param }
,留下 keyword_arg
nil
。
这是解析器错误还是预期行为?在 ruby 2.3.0 和 2.2.4.
上测试编辑:将第一个参数设置为强制性,一切都像我期望的那样:
def test_mandatory(first_param, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
test_mandatory(first_param: :is_a_hash)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg:
test_mandatory(keyword_arg: :should_be_first_param)
#=> first_param: {:keyword_arg=>:should_be_first_param}
# keyword_arg:
我希望将参数设为可选不会改变参数的解析方式。
我打开了一个 issue on bugs.ruby-lang.org,然后开发人员可以弄清楚它是这样设计的还是 kword args 的副作用。
根据Marc-Andre Lafortune's reply预计:
This behavior may be surprising but it is intentional.
It boils down to giving priority to filling keyword arguments first instead of filling unnamed parameters. It is actually the only possible way to go. Among other things, think about the following example:
def foo(*rest, bar: 42) end
If we don't prioritize named arguments first, then there is simply no way to specify a value for bar in this example!
So Ruby checks that:
- after all mandatory unnamed arguments are filled
- if the last remaining argument is hash-like
- and all its keys are symbols
- and the method called uses keyword arguments
=> then that parameter is used for keyword arguments.
Note the requirement on keys being symbols. This can yield even more surprising if you pass a hash with some keys that are not symbols:
def foo(a = nil, b: nil) p a, b end foo(:b => 42) # => nil, 42 foo('b' => 42) # => {"b" => 42}, nil