如何从 splatted kwargs 字段中检索关键字参数?

Mas*_*son 9 julia

如果我有一个像 的函数签名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
Run Code Online (Sandbox Code Playgroud)

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

Mas*_*son 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}}}
Run Code Online (Sandbox Code Playgroud)

因此,splatted kwargs 被存储为某种迭代器对象。但是,我们可以轻松地将该kwargs迭代器转换为 NamedTuple,如下所示:(;kwargs...)然后以我们期望的方式访问它,因此您的示例将转换为

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

julia> f(x=1, y=2)
1
Run Code Online (Sandbox Code Playgroud)

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

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

julia> f(x=1, y=2)
1
Run Code Online (Sandbox Code Playgroud)

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


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

julia> g(x=1, y=2) |> typeof |> fieldnames
(:data, :itr)
Run Code Online (Sandbox Code Playgroud)

嗯,这是什么data领域?

julia> g(x=1, y=2).data
(x = 1, y = 2)
Run Code Online (Sandbox Code Playgroud)

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