Como recupero argumentos de palavras-chave de um campo de kwargs splatted?

9

Se eu tiver uma assinatura de função como f(args...; kwargs...), como posso obter uma palavra-chave específica kwargs? Digitação ingênua kwargs.xnão funciona:

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

Esta pergunta apareceu no canal JuliaLang Slack no #helpdesk. Para um convite automático para a muito útil folga de julia, basta preencher https://slackinvite.julialang.org

Pedreiro
fonte

Respostas:

10

A razão disso acontecer é que os argumentos de palavras-chave divididas não são armazenados em uma tupla nomeada por padrão. Podemos ver como eles são armazenados assim:

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}}}

Portanto, os kwargs splatted são armazenados como algum tipo de objeto iterador. No entanto, podemos facilmente converter esse kwargsiterador em um NamedTuple da seguinte forma: (;kwargs...)e depois acessá-lo da maneira que esperávamos, para que seu exemplo se traduza em

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

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

Obviamente, a maneira mais idiomática de fazer isso seria escrever a função como

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

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

mas isso pressupõe que você saiba o nome que deseja acessar ( x) no momento em que escreve a função.


Uma breve nota lateral: se retornarmos ao nosso exemplo de g(;kwargs...) = kwargs, podemos solicitar os nomes de campo do objeto iterador que foi retornado da seguinte forma:

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

Hum, que datacampo é esse ?

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

Aha! para que possamos obter os kwargs como uma tupla nomeada usando isso, ou seja f(;kwargs...) = kwargs.data.x, funcionaria, mas eu não recomendaria essa abordagem, pois parece depender de comportamento não documentado, portanto, pode ser um mero detalhe de implementação que não garante a estabilidade. nas versões julia.

Pedreiro
fonte