Как я могу получить аргументы ключевых слов из поля разделенных кваргами?


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

Этот вопрос появился на канале JuliaLang Slack в #helpdesk. Чтобы автоматически пригласить очень полезную Джулию Слак, просто заполните https://slackinvite.julialang.org

Ответы:


10

Причина, по которой это происходит, заключается в том, что аргументы с разделенными ключевыми словами по умолчанию не хранятся в именованном кортеже. Мы можем видеть, как они хранятся так:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.