Comment puis-je récupérer des arguments de mots clés dans un champ de kwargs éclaboussés?

9

Si j'ai une signature de fonction comme f(args...; kwargs...), comment puis-je obtenir un mot clé spécifique kwargs? La saisie naïve kwargs.xne fonctionne pas:

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

Cette question est apparue sur la chaîne JuliaLang Slack dans le #helpdesk. Pour une invitation automatique au très utile julia slack, remplissez simplement https://slackinvite.julialang.org

le maçon
la source

Réponses:

10

La raison en est que les arguments de mots clés éclaboussés ne sont pas stockés par défaut dans un tuple nommé. Nous pouvons voir comment ils sont stockés comme suit:

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

Ainsi, les kwargs éclaboussés sont stockés à la place comme une sorte d'objet itérateur. Cependant, nous pouvons facilement convertir cet kwargsitérateur en NamedTuple comme ceci: (;kwargs...)puis y accéder de la manière attendue, de sorte que votre exemple se traduira par

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

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

Bien sûr, la façon la plus idiomatique de le faire serait d'écrire la fonction à la place

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

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

mais cela suppose que vous connaissez le nom xauquel vous souhaitez accéder ( ) au moment où vous écrivez la fonction.


Une brève note: si nous revenons à notre exemple de g(;kwargs...) = kwargs, nous pouvons demander les noms de champs de l'objet itérateur qui a été renvoyé comme suit:

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

Hm, quel est ce datachamp?

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

Ah! donc nous pouvons réellement obtenir les kwargs en tant que tuple nommé en utilisant cela, c'est-à f(;kwargs...) = kwargs.data.x- dire que cela fonctionnerait, mais je ne recommanderais pas cette approche car elle semble s'appuyer sur un comportement non documenté, il peut donc s'agir d'un simple détail d'implémentation qui n'est pas garanti d'être stable à travers les versions de julia.

le maçon
la source