Check if keyword arguments exist in Julia
Asked Answered
J

4

5

I have a function collect extra keyword arguments using ..., so it is like function f(args=0; kwargs...). I want to check if a keyword argument, let' say, a, exists in kwargs.

What I do probably is not an elegant way, I first create a Dict to store the keywords and corresponding values kwargs_dict=[key=>value for (key, value) in kwargs], then I use haskey(kwargs_dict, :a) to check if a is a key in the dict. Then I get its value by kwargs_dict[:a].

function f(; kwargs...)
   kwargs_dict = [key=>value for (key, value) in kwargs]
   haskey(kwargs_dict, :a)
   a_value = kwargs_dict[:a]
end

f(args=0, a=2)
> true

f(args=0)
> false

I wonder if there is better way to check if the keyword argument a is in kwargs and to get the value of the existed keyword argument.

Jennettejenni answered 2/2, 2016 at 15:15 Comment(0)
R
4

Another method is to use default values for named keyword variables. With the default being a special value (a.k.a sentinel value). When a value is supplied by the user it overrides the default and this can easily be checked. nothing is a commonly used value. Specifically, for the example in the question:

julia> function g(; a = nothing, kwargs...)
    kwargs_dict = [key=>value for (key, value) in kwargs]
    a != nothing
end
g (generic function with 1 method)

Gives the functionality:

julia> g(args=0, a=2)
true
julia> g(args=0)
false

The advantage is 1st speed - default valued keyword arguments can avoid the kwargs dictionary and if possible optimization will drop the whole dictionary setup code. The 2nd advantage could be readability (but to each his own taste).

Royalroyalist answered 2/2, 2016 at 17:31 Comment(1)
This is a good approach in many cases, but make sure to watch out for type instabilities: ` #= good =# f(;a::Int=0) = a; g(use::Bool) = use ? f(a=1) : f(); @code_warntype g(true); #= bad =# f(;a=nothing) = a==nothing ? 0 : a; g(use::Bool) = use ? f(a=1) : f(); @code_warntype g(true) `Ascomycete
O
7

You can just pass kwargs to the Dict constructor to get a dictionary representation of the kwargs. For example:

kwargs_dict = Dict(kwargs)
Odontoid answered 2/2, 2016 at 16:18 Comment(1)
Thanks that is an elegant way to make the dict.Jennettejenni
R
4

Another method is to use default values for named keyword variables. With the default being a special value (a.k.a sentinel value). When a value is supplied by the user it overrides the default and this can easily be checked. nothing is a commonly used value. Specifically, for the example in the question:

julia> function g(; a = nothing, kwargs...)
    kwargs_dict = [key=>value for (key, value) in kwargs]
    a != nothing
end
g (generic function with 1 method)

Gives the functionality:

julia> g(args=0, a=2)
true
julia> g(args=0)
false

The advantage is 1st speed - default valued keyword arguments can avoid the kwargs dictionary and if possible optimization will drop the whole dictionary setup code. The 2nd advantage could be readability (but to each his own taste).

Royalroyalist answered 2/2, 2016 at 17:31 Comment(1)
This is a good approach in many cases, but make sure to watch out for type instabilities: ` #= good =# f(;a::Int=0) = a; g(use::Bool) = use ? f(a=1) : f(); @code_warntype g(true); #= bad =# f(;a=nothing) = a==nothing ? 0 : a; g(use::Bool) = use ? f(a=1) : f(); @code_warntype g(true) `Ascomycete
H
1

My two cents: You could also define

function f(; kwargs...)
    keys = [k for (k, v) ∈ kwargs]
    :a ∈ keys
end

If you just want to provide a value in case no kwarg is supplied, you can also do

function f(; kwargs...)
    a = get(kwargs, :a, "my default value for a")
end

which allows you to

julia> f(ab = "a")
"my default value for a"

julia> f(a = "a")
"a"
Heartstricken answered 17/3, 2022 at 9:37 Comment(0)
B
1

I think the most straightforward and allocation-free method (since at least Julia 1.6) is to check

:a in keys(kwargs)

If true, you can retrieve the value with

a = values(kwargs).a

Here is a slightly modified version of your example:

function f(; kwargs...)
    if :a in keys(kwargs)
        a = values(kwargs).a
        if a > 0
            return true
        end
    end
    return false
end

julia> @btime f(a=2)
  0.900 ns (0 allocations: 0 bytes)
true

julia> @btime f(b=2)
  0.900 ns (0 allocations: 0 bytes)
false
Bunde answered 8/1, 2023 at 11:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.