4

Question "Is there a way to pass optional parameters to a function?" describes how default keyword parameters are used.

I have two functions, one of which calls the other, and I want to be able to detect if the outer function was called with an optional parameter:

def foo(x1, opt=None):
    bar(opt)

def bar(opt=5):
    print opt

if __name__ == '__main__':
    foo(1)           # Should print 5, actually prints None
    foo(2, opt=3)    # Does print 3

This prints:

None
3

Foo() could be written with special logic to detect whether the optional parameter is passed, but is there a pythonic way to pass an optional parameter with its optionality preserved without using extra logic?

def foo2(x1, opt=None):
    if opt is None:
        bar()
    else:
        bar(opt)

The default has to be defined at the inner function because it could be called directly. I could also define the same default for inner and outer functions, but that's a bit ugly (violates DRY) when the default is more complex than an integer.

Dave
  • 3,834
  • 2
  • 29
  • 44
  • 1
    I'm just struggling with the question: do you have a design problem, if you need to define two different default values for inner and outer functions? – tangoal May 23 '18 at 19:08
  • Does this help: https://docs.python.org/3/faq/programming.html#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another – KolaB May 23 '18 at 19:10
  • I think it would help if you explain the context by perhaps rephrasing your code to use meaningful function and argument names instead of `foo`, `x1`, `opt` etc. I understand literally what you're asking, but it's hard to offer opinion without understanding the actual problem you're trying to solve. – NPE May 23 '18 at 19:12
  • @NPE, meaningless names are the simplest way to reproduce the problem. The actual use case is for the inner function to produce a (very long) string given some arguments, and for the outer function to write a file containing that output given those arguments plus a filename. To be even more specific, the contents of the string/file is an HTML5 document. – Dave May 23 '18 at 19:21

5 Answers5

2

One thing that you could do is use the **kwargs syntax to allow for any keyword arguments to be passed through, and then just raise errors if there are any extra keywords that don't mean anything (or alternatively, just cleanly ignore them, though that confuses users in case they make a typo and no error occurs). In the case of cleanly ignoring them:

def foo(x1, **kwargs):
    if "opt" in kwargs:
        bar(kwargs["opt"])
    else:
        bar()

Basically, the **kwargs lets you pass in any keyword arguments like foo(opt = 3) and puts them all in a dict. The downside to this is that you cannot call it like foo(3). If you want to go by placement arguments instead of keyword arguments, you can do *args which gives a tuple of the arguments, but then you are unable to call it with the keyword specified. You cannot do both, unfortunately.

hyper-neutrino
  • 5,272
  • 2
  • 29
  • 50
1

Since opt is not specified, it defaults to None as you have seen. This gives a definition to opt, and it does not remember or retain any property that it is a default value, so no, implementing logic here is mandatory for the required result.

Riolku
  • 572
  • 1
  • 4
  • 10
1

Perhaps just set the default argument in both places:

DEFAULT_OPT = 5

def foo(x1, opt=DEFAULT_OPT):
    bar(opt)

def bar(opt=DEFAULT_OPT):
    print opt 

Otherwise use kwargs which I tend to avoid for being hard to maintain:

def foo(x1, **kwargs):
    bar(**kwargs)

def bar(opt=5, **_):
    print opt 
Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88
0

In foo you call bar with opt. This overwrites the default value of opt inside bar.

dangee1705
  • 3,445
  • 1
  • 21
  • 40
0

None is an object in Python (see here) This means that it is a valid argument, when you pass None to bar. That's why it does not trigger the default parameter value 5. I don't know anything else in Python, which may do so.

tangoal
  • 724
  • 1
  • 9
  • 28