Ruby 2.0.0推出了一个新的特性Keyword Argument,中文名可能叫关键字参数或者命名参数,我在这里详细介绍下这个特性的具体细节。
什么是Keyword Argument呢?
在这里通过一个例子说明:
1
2
3
4
| def log(msg, level: "ERROR", time: Time.now)
puts "#{ time.ctime } [#{ level }] #{ msg }"
end
log("Hello!", level: "INFO") #=> Mon Feb 18 01:46:22 2013 [INFO] Hello!
|
看起来很普通好像没有什么特别新的,是不是?
详细解释
在Ruby 1.9里面也有下面的调用方法方式:
1
| log("Hello!", level: "INFO")
|
上面传的第二个参数是hash,定义像下面这样:
1
2
3
4
5
| def log(msg, opt = {})
level = opt[:level] || "ERROR"
time = opt[:time] || Time.now
puts "#{ time.ctime } [#{ level }] #{ msg }"
end
|
但是也许需求不是那么简单:
- 希望hash如果传给我们不期望的key,需要抛出异常
- 希望hash的其中一个key可以传nil
那我们需要把方法改成如下:
1
2
3
4
5
6
7
| def log(*msgs)
opt = msgs.last.is_a?(Hash) ? msgs.pop : {}
level = opt.key?(:level) ? opt.delete(:level) : "ERROR"
time = opt.key?(:time ) ? opt.delete(:time ) : Time.now
raise "unknown keyword: #{ opt.keys.first }" if !opt.empty?
msgs.each {|msg| puts "#{ time.ctime } [#{ level }] #{ msg }" }
end
|
如果你处理类似需求的方法,会不会觉得很麻烦。如果使用Ruby 2.0 的新功能Keyword arguments,代码将会变得下面这么简单干净:
1
2
3
| def log(msg, level: "ERROR", time: Time.now)
puts "#{ time.ctime } [#{ level }] #{ msg }"
end
|
更多细节
我们可以调用的时候,参数部分省略
1
2
| log("Hello!") #=> Mon Feb 18 01:46:22 2013 [ERROR] Hello!
log("Hello!", level: "ERROR", time: Time.now) #=> Mon Feb 18 01:46:22 2013 [ERROR] Hello!
|
Keyword argument的顺序不是很重要,但其他参数的顺序是不可以随便写的。
1
2
| log("Hello!", time: Time.now, level: "ERROR") #=> Mon Feb 18 01:46:22 2013 [ERROR] Hello!
log(level: "ERROR", time: Time.now, "Hello!") # 运行错误
|
但如果你传入一个非期待的key时,将会报错
1
| log("Hello!", date: Time.new) #=> unknown keyword: date
|
如果你不想这个非期待的key报错,需要加入一个**开头的参数来保存这个key,
1
2
3
4
5
| def log(msg, level: "ERROR", time: Time.now, **kwrest)
puts "#{ time.ctime } [#{ level }] #{ msg }"
end
log("Hello!", date: Time.now) #=> Mon Feb 18 01:46:22 2013 [ERROR] Hello!
|
当然你可以将Keyword argument和可变参数混合放一起使用,不太建议这么使用,会减少代码的可读性,增加了复杂度,容易出现bug。
1
2
3
| def f(a, b, c, m = 1, n = 1, *rest, x, y, z, k: 1, **kwrest, &blk)
...
end
|
限制
可变参数和Keyword arguments混用的时候,容易出问题。例子:
1
2
3
4
5
6
7
| def foo(*args, k: 1)
p args
end
args = [{}, {}, {}]
foo(*args) #=> [{}, {}]
|
可变参数的最后一个值被赋给keyword arguments了
其次你也不能把**作为参数
1
2
3
| def foo(**)
end
foo(k: 1) #=> unknown keyword: k
|
还有参数的key不能ruby里面的保留关键字,例:
1
2
3
| def foo(if: false)
end
foo(if: true)
|
如果想使用if
作为key,只能使用**
参数如下:
1
2
3
4
| def foo(**kwrest)
p kwrest[:if]
end
foo(if: true) #=> true
|