functions, optional parameters

Chris Angelico rosuav at gmail.com
Fri May 8 08:39:17 EDT 2015


On Fri, May 8, 2015 at 9:59 PM, Michael Welle <mwe012008 at gmx.net> wrote:
> Hello,
>
> assume the following function definition:
>
> def bar(foo = []):
>     print("foo: %s" % foo)
>     foo.append("foo")
>
> It doesn't work like one would expect (or as I would expect ;-)). As I
> understand it the assignment of the empty list to the optional parameter
> foo take place when the function object is created, not when it is
> called. I think from the perspective of a user this is very strange.
> Anyways, what would be a good idiom to get the desired behaviour?

I'm not sure what the desired behaviour _is_, given that you're not
doing anything with the list after appending to it. But argument
defaults are evaluated when the function's defined. It's basically
like this:

DEFAULT_FOO = []
def bar(foo | nothing):
    if no argument passed: foo = DEFAULT_FOO
    print("foo: %s" % foo)
    foo.append("foo")

There are times when this is incredibly useful, but if you don't want
this behaviour, the most common idiom is this:

def bar(foo=None):
    if foo is None: foo = []
    print("foo: %s" % foo)
    foo.append("foo")

As an example of this, a project I'm involved in had a transition
function which could work in one of two modes:

1) Transition one file into another
2) Transition one file into another, then that into a third, then a fourth, etc

In order to maintain state cleanly, a dictionary was passed in, which
provided a "previous position" marker, which could then be updated.
For the first form, though, all I needed to do was give it an empty
dictionary, which would then be discarded. So the function went
something like this:

def transition(from, to, state=None):
    if not state: state={"cursor": 0}
    # ...
    state["cursor"] = last_position

(I can't easily show you the code; subsequent edits meant that the
first case actually wanted to retrieve the cursor position, so it now
always has a state dict, and has no default argument. But this is
still a valid concept.)

Both idioms are very common, and you need to decide whether you want a
single mutable default, or a None default that gets replaced by a
brand new dict/list/etc every time. Just remember that the
construction of a new list is quite different from the assignment of a
pre-existing list to a new name.

ChrisA



More information about the Python-list mailing list