2

TLDR: I want to pass an argument to a function which forces the function to use it's default, as if I didn't supply any arguments.

For example

def foo(string = "DEFAULT PARAM"):
    print(string)

def bar(string = None):
    foo(string)

bar()            # Should print "DEFAULT PARAM", but instead prints None
bar("Hello!")    # Should print "Hello!"

I know there are a lot of workarounds like:

def bar(string = None):
    foo() if string is None else foo(string)

But I was hoping for something more elegant. Like some kind of default keyword I can set bar's default string to. Is that a thing?

AaronCoding
  • 158
  • 1
  • 6

4 Answers4

7

I have encountered this annoyance many times in Python (mostly when using argparse, which populates omitted options with null values instead of just omitting them from the namespace).

When you want to explicitly pass None as a value, and have it be treated the same as if it was just omitted completely, this is best:

def foo(string=None):
    if string is None:
        string = "DEFAULT PARAM"
    print(string)

def bar(string=None):
    foo(string)

The opposite annoyance is when None is a valid value, and you actually need to pass it explicitly sometimes. In that case, this is best:

sentinel = object()

def foo(arg=sentinel):
    if arg is sentinel:
        arg = "DEFAULT PARAM"
    print(arg)

def bar(arg=None):
    foo(arg)
wim
  • 338,267
  • 99
  • 616
  • 750
6

No, there is no value that you can pass that will trigger the default value to be used in place of the passed value. Passing a value binds that value to the formal parameter, period.

chepner
  • 497,756
  • 71
  • 530
  • 681
0

You can't. I believe that there have been discussions about adding a new singleton to use in cases where None is an acceptable value to pass. To my knowledge, those proposals have always been shot down because python has plenty of singletons already:

NotImplemented
Ellipsis
None

Your proposed solution is what is typical with mutable default arguments so it also makes sense to use it for other cases as well (like this one). Obviously there are times when None is a reasonable value to pass -- In that case, choose another singleton or create your own (via object()).


Your final recourse (i.e. when you do not have control over the function and when you simply can't call the function without passing an argument) is to parse the default value from the function itself and pass that.

Obviously, this approach should be used sparingly :-)

mgilson
  • 300,191
  • 65
  • 633
  • 696
-1
def foo(string = 'default'):
    print(string)

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

bar() # prints 'default'
bar(string = 'baz') # prints 'baz'
Alex Undefined
  • 620
  • 1
  • 5
  • 8
  • -1 This is a breaking change on the `bar` function. Previously you could call it with positional argument, and now you can't. It's also poor style - using `**kwargs` for trivial reasons is a great way to make your library code unreadable and turn it into a maintenance nightmare. **Avoid!** – wim Aug 25 '17 at 01:28
  • 1
    Making mistakes is integral to the learning process. If people want to touch a hot stove, just let them do it, don't evangelize. – Alex Undefined Aug 25 '17 at 06:25