44

Suppose I have a python function that takes two arguments, but I want the second arg to be optional, with the default being whatever was passed as the first argument. So, I want to do something like this:

def myfunc(arg1, arg2=arg1):
    print (arg1, arg2)

Except that doesn't work. The only workaround I can think of is this:

def myfunc(arg1, arg2=None):
    if arg2 is None:
        arg2 = arg1
    print (arg1, arg2)

Is there a better way to do this?

2
  • I'm just rethinking this, it might be possible to achieve with a decorator. It's going to be unclear what it does at first glance so probably a bad idea, but it's an idea. Commented Jan 1, 2011 at 19:39
  • Not directly in the function header line, no. But inside the function body, by testing if argn is None: argn = <default_expression_involving_other_args>, yes. Commented Jul 18, 2019 at 2:09

4 Answers 4

30

As @Ignacio says, you can't do this. In your latter example, you might have a situation where None is a valid value for arg2. If this is the case, you can use a sentinel value:

sentinel = object()
def myfunc(arg1, arg2=sentinel):
    if arg2 is sentinel:
        arg2 = arg1
    print (arg1, arg2)

myfunc("foo")           # Prints 'foo foo'
myfunc("foo", None)     # Prints 'foo None'
Sign up to request clarification or add additional context in comments.

1 Comment

probably better to just default arg2 to None and check that instead of bothering with a custom sentinel object unless you need to handle user provided None different from default
8

Not really. The other argument names don't exist until the function is actually entered.

2 Comments

But you can tidy it up slightly by changing the if clause in the workaround to: arg2 = arg2 or arg1
@Michael That will fail if arg2 is 0, False or an empty sequence.
4

The second way is fine in my opinion: it is exactly clear what you do, and for all those who will come after you and read the code. While there is a slight overhead in documenting the default behavior, the use of 'None' or any other to make your function generate a default value is very common. Putting logic inside the function calls would certainly not be commendable in my opinion, it could become very complex very soon. So just leave it in the function body, where all the code is.

Comments

0

In addition to the working example you included, this can also be done using **kwargs:

def myfunc(arg1, **kwargs):
    arg2 = kwargs.get('arg2', arg1)
    print (arg1, arg2)

myfunc("foo")           # Prints 'foo foo'
myfunc("foo", "bar")    # Prints 'foo bar'
myfunc("foo", None)     # Prints 'foo foo'

This is, in my opinion, more Pythonic than using a placeholder-default (such as suggested in @moinudin's answer). However, using kwargs hides the existence of args2 from consumers of the function, unless you provide extra documentation.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.