BruceWayne
BruceWayne

Reputation: 23283

Add an argument to a function's default arguments?

I'm wondering if there's a way to add an argument to the default arguments a function takes.

For instance:

SOME_CITIES = ["Gotham","Chicago"]
def do_something(name, cities=SOME_CITIES):
    for city in cities:
        print(name, city)

do_something("Clark")
>> Clark Gotham
>> Clark Chicago

# Can I do something like this (pseudocode, obviously):
do_something("Clark", *["New York", "LA"])
>> Clark Gotham
>> Clark Chicago
>> Clark New York
>> Clark LA

The idea is do_something("Clark", *"New York") would run through the function, doing Gotham, Chicago, and then the city I added, New York.

This is similar from my understanding of **kwargs, but that would look like:

def do_something(name, **kwargs):
    # Code here...

do_something("Clark", SOME_CITIES, "New York")

But that requires me to recall to send SOME_CITIES through, when I'd like to include that by default. Does that make sense?

Edit: The SOME_CITIES may not always be a list. It could be two functions, i.e. SOME_FUNCTIONS = func1, func2. Also - as some answers seem to get at below, if this is a bad idea and kwargs/args is better, that's fine too just let me know and explain a little :)

Upvotes: 0

Views: 89

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1121346

Python has no syntax or behaviour to extend lists used as a default argument, no. You can simply use the *args catch-all parameter here and concatenate in the function:

SOME_CITIES = ("Gotham", "Chicago")
def do_something(name, cities=SOME_CITIES, *further_cities):
    for city in cities + further_cities:
        print(name, city)

If the sequences are expected to be large, you could use itertools.chain() instead of concatenation. Note the change to using tuples here, which avoids a common pitfall of using a mutable data type where you never intent to make changes to the value at runtime anyway.

You need to be extremely careful when using a list as a default argument value, see "Least Astonishment" and the Mutable Default Argument. In this specific case there is little point in avoiding it as SOME_CITIES is also defined outside of the function and you have more options to alter that object, but you may want to consider using a None sentinel instead:

SOME_CITIES = ["Gotham", "Chicago"]
def do_something(name, cities=None, *further_cities):
    if cities is None:
        # start with a base list
        cities = SOME_CITIES
    cities = cities + list(further_cities)
    for city in cities:
        print(name, city)

Upvotes: 2

Ajax1234
Ajax1234

Reputation: 71451

You can change your default parameter in your function to *args, and then use functools.partial:

import functools
SOME_CITIES = ["Gotham","Chicago"]
OTHER_CITIES = ["New York", "LA"]
def do_something(name, *cities):
  for city in cities:
     print(name, city)

new_funct = functools.partial(do_something, 'clark', *SOME_CITIES)
new_funct(*OTHER_CITIES)

Output:

clark Gotham
clark Chicago
clark New York
clark LA

Upvotes: 1

Related Questions