如何从出现过的kwarg字段中检索关键字参数?


9

如果我有类似的函数签名f(args...; kwargs...),如何从中获得特定的关键字kwargs?单纯键入 kwargs.x不起作用:

julia> f(args...; kwargs...) = kwargs.x
f (generic function with 1 method)

julia> f(x=1)
ERROR: type Pairs has no field x
Stacktrace:
 [1] getproperty(::Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol},NamedTuple{(:x,),Tuple{Int64}}}, ::Symbol) at ./Base.jl:20
 [2] #f#7(::Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol},NamedTuple{(:x,),Tuple{Int64}}}, ::typeof(f)) at ./REPL[2]:1
 [3] (::var"#kw##f")(::NamedTuple{(:x,),Tuple{Int64}}, ::typeof(f)) at ./none:0
 [4] top-level scope at REPL[3]:1

这个问题出现在#helpdesk的JuliaLang Slack频道中。要自动邀请非常有用的julia slack,只需填写https://slackinvite.julialang.org

Answers:


10

发生这种情况的原因是,默认情况下,splatted关键字参数未存储在命名元组中。我们可以看到它们的存储方式:

julia> g(;kwargs...) = kwargs
g (generic function with 1 method)

julia> g(a=1)
pairs(::NamedTuple) with 1 entry:
  :a => 1

julia> g(a=1) |> typeof
Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol},NamedTuple{(:a,),Tuple{Int64}}}

因此,分散的kwarg会存储为某种迭代器对象。但是,我们可以kwargs像这样轻松地将该迭代器转换为NamedTuple:(;kwargs...)然后按照我们期望的方式访问它,因此您的示例将转换为

julia> f(args...; kwargs...) = (;kwargs...).x
f (generic function with 1 method)

julia> f(x=1, y=2)
1

当然,更惯用的方法是将函数写为

julia> f(args...; x, kwargs...) = x
f (generic function with 1 method)

julia> f(x=1, y=2)
1

但这是假设您x在编写函数时知道要访问的名称()。


一个简短的旁注:如果返回到示例g(;kwargs...) = kwargs,我们可以要求返回迭代器对象的字段名,如下所示:

julia> g(x=1, y=2) |> typeof |> fieldnames
(:data, :itr)

嗯,这是什么data领域?

julia> g(x=1, y=2).data
(x = 1, y = 2)

啊哈!因此我们实际上可以使用kwargs作为命名元组,即f(;kwargs...) = kwargs.data.x可以,但是我不推荐这种方法,因为它似乎依赖于未记录的行为,因此它可能只是一个实现细节,不能保证稳定跨julia版本。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.