from typing import Optional, List
from numbers import Real
def mul(a, m:Optional[List[Real]]=None):
m = [1,2] if m is None else m
return a*m[0], a*m[1]
Here, m has a default value of None which is then made into [1,2]. This is seen as good practice as def mul(a, m = [1,2]): can lead to unpredictable values of m. Additionally, m can take the form of any Real numbers (floats and ints).
In Julia I have
function mul(a, m::Vector{Real}=[1,2])
return a*m[1], a*m[2]
end
calling
julia> mul(2)
ERROR: MethodError: no method matching mul(::Int64, ::Vector{Int64})
julia> Int64 <: Real
true
This confuses me. So by default m is a Vector{Int64} but which Julia says there’s no method for even though Int64 is a subtype of Real. There are two questions here:
How can I correctly label mul as requiring a vector of real numbers?
In Julia is it safe to have mutable objects as default values? mul(a, m=[1,2])
mul(a, m::Vector{<:Real}=[1,2])
# or
mul(a, m::Vector{T}=[1,2]) where {T<:Real}
The former is just syntactic sugar for the latter.
As for your second question, I’m not sure I understand. Safe in what way? I wouldn’t hesitate to use it, anyway. Why is the python version with None as default preferred?
BTW, since your function anyway returns a tuple, maybe you should allow tuple input?
Thank you. I’ve seen functions declared in this way but never really understood what is going on. I’ll implement this.
In place operations can cause unexpected behaviour as the default argument isn’t set each time the function is called, whereas it is when using None as default. Therefore, if mutable objects are the default and they’re changed inplace unexpected things can happen. See this silly example
I must say, I find this pretty terrible. Isn’t this basically a bug? I mean, I know it is expected and probably documented, but it’s so strange and unnatural. It’s like a bug you can’t fix, so you just have to document it as “This doesn’t work right.”
Or does there exist some justification why this could be beneficial?
I didn’t realise you can doo foo(x, y=2x), I like that.
I think mutable default arguments has been discussed a lot this stack is 13 years old and references the official docs, it can also cause things to be a bit of mess if you use lru_cache. In general my python experience has made me very weary of inplace operations, which Julia uses extensively as they have benefits. But I still feel uncomfortable with them.
I’m not sure what the advantage would be, maybe saving a tiny bit of memory but at the cost of breaking “what you see is what you get”