Причина, по которой это происходит, заключается в том, что аргументы с разделенными ключевыми словами по умолчанию не хранятся в именованном кортеже. Мы можем видеть, как они хранятся так:
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}}}
Таким образом, выделенные кварги вместо этого хранятся как некий объект итератора. Тем не менее, мы можем легко преобразовать этот 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
будет работать, но я бы не рекомендовал этот подход, так как он, кажется, полагается на недокументированное поведение, так что это может быть простой деталью реализации, которая не гарантируется стабильной по версиям джулия.