class A
def a
1
end
end
a = A.new
x = {}
a.a(**x) # => 1 in both Ruby 2.6 and 2.7
a.public_send(:a, **x) # => 1 in Ruby 2.7
然而,在Ruby 2.6中:
ArgumentError: wrong number of arguments (given 1, expected 0)
这是2.7版之前的public_send
/send
/__send__
中的一个错误吗?你有什么建议来克服这种差异?
你可以在这里查看这个失败的直播。
在Ruby 2.6及之前的版本中,**argument
语法主要(但不完全(是传递哈希的语法糖。这样做是为了保持将变量哈希作为最后一个参数传递给方法的约定有效。
然而,在Ruby2.7中,关键字参数进行了语义更新,不再映射到哈希参数。这里,关键字参数是从位置参数处理的。
在Ruby 2.6及之前的版本中,以下两个方法定义(至少在许多方面(是等效的:
def one(args={})
#...
end
def two(**args)
#...
end
在这两种情况下,您都可以传递一个逐字的散列或一个具有相同结果的飞溅散列:
arguments = {foo: :bar}
one(arguments)
one(**arguments)
two(arguments)
two(**arguments)
然而,对于Ruby 2.7,您应该按原样传递关键字参数(前面的行为仍然有效,但已被弃用并带有警告(。因此,对two(arguments)
的调用将导致2.7中的弃用警告,并且在Ruby 3.0中将变为无效。
因此,在Ruby 2.7中,一个飞溅的散列参数(将关键字参数传递给方法(会导致一个空的关键字参数列表,而在2.6中则会产生一个带有空散列的位置参数。
您可以通过验证Ruby如何解释其public_send
方法的参数来详细了解这里发生了什么。在Ruby 2.6及更早版本中,该方法有效地具有以下接口:
def public_send26(method_name, *args, &block);
p method_name
p args
# we then effectively call
# self.method_name(*args, &block)
# internally from C code
nil
end
当在Ruby2.6中以public_send26(:a, **{}
的形式调用此方法时,您将看到关键字参数再次被"包装"在Hash:中
:a
[{}]
使用Ruby 2.7,您可以使用以下有效的界面:
def public_send27(method_name, *args, **kwargs, &block);
p method_name
p args
p **kwargs
# Here, we then effectively call
# self.method_name(*args, **kwargs, &block)
# internally from C code
nil
end
您可以看到,在Ruby 2.7中,关键字参数被单独处理并保留为关键字参数,而不是像Ruby 2.6和更早版本中那样,将它们作为方法的常规位置Hash参数处理。
Ruby 2.7仍然包含回退行为,因此期望Ruby 2.6行为的代码仍然可以工作(尽管有警告(。在Ruby 3.0中,必须严格地将关键字参数和位置参数分开。你可以在ruby-lang.org上的新闻条目中找到这些变化的一些额外描述。