The focus of my question is on design.
If I have an argument that can be None
and it is passed between several functions until finally being used, which function should treat it as a default argument?
For example, suppose the following code block:
def foo(a):
b = # some code block to assign b
return bar(a, b)
def bar(a, b):
c = # some code block to assign c
return baz(a, b, c)
def baz(a, b, c):
return a + b + c
The default value should be set only in foo
and all the other functions must expect the argument as mandatory (e.g., foo(a=None); bar(a, b); baz(a, b, c)
)? Or is there a better design?
And if type verification was needed, what is the most suitable function for this?
1 Answer 1
To make a sensible decisions about default arguments, one has to know the context how these functions are going to be used!.
If foo
is going to be part of the public API of a module or component, then providing a default argument a=None
assumes there is a frequent usage scenario where foo()
will be called in this form, without any parameters. The same measure holds for bar
and baz
:
are those functions are just used "internally"? Then you know precisely whether providing a default argument will save you some repetitional code, or if
bar(a, b)
is only called once or twice, maybe only withinfoo
, in which case a default argument is not beneficialor are those functions also part of the public API? Then you need to estimate how their typical usage scenario will look like (for which you need some real context). If you want to provide the ability for
bar
to be called likebar(b="some value")
, then providing the signaturebar(a=none, b)
is necessary, regardless of the default parameter infoo
.
So for making decisions about default arguments, don't look at the implementation side, look at the usage side!
Concerning type verification: I am not an expert for this topic in Python, but I guess if you want to add type annotations for static type analysis, IMHO it will make most sense to add them at every level of the call chain (it may be actually mandatory for making the type analysis working) . If the "innermost function" (baz
) expects only arguments of a certain type, you obviously have to put the type annotations there. This, however, implies that foo
and bar
will only work correctly with arguments of the same type, which means for static type analysis the annotations are required there, too.
-
In reference to your last sentence, by default
foo
will not give warnings, but some static type analyzers have strict options to force a type annotation to be present everywhere, causing an unannotatedfoo
to be an error.Mario Ishac– Mario Ishac2020年09月10日 05:00:23 +00:00Commented Sep 10, 2020 at 5:00 -
@MarioIshac: I suspected that, changed my answer accordingly, thanks!Doc Brown– Doc Brown2020年09月10日 06:05:34 +00:00Commented Sep 10, 2020 at 6:05