What is a function parameter =[] for?

Chris Angelico rosuav at gmail.com
Wed Nov 18 20:59:18 EST 2015


On Thu, Nov 19, 2015 at 12:41 PM, BartC <bc at freeuk.com> wrote:
> On 18/11/2015 23:22, Chris Angelico wrote:
>> On the contrary, it is certain always to be that exact object.
>
> But, not the same value that appears as the default?

Nope. Mutable objects are never guaranteed to retain the same values.
That's kinda the point.

> Sorry, I didn't understand any of that. Let's try a simpler example like the
> OP's. There's this innocuous looking function that you are interested in
> calling:
>
>  def fn(a=[]):
>  #   print ("Function fn called with",a)
>  |   a.append(10)
>  |   return a
>
> It appends a value to its argument and returns the result.
>
> It looks like at first like, if called with no argument, it will return
> [10].

If you want to think of function defaults as being values, then yes.
But they're not. They're objects.

> But what it actually returns depends on the entire call history since the
> program started executing, something you probably have no knowledge of and
> no control over. So if fn has previously been called a million times with no
> argument, then after this call:
>
>  x=fn()
>
> x contains a list of a million tens, not one. I'd say most people would be
> rather surprised by that.

So if you don't want them to be surprised by that, don't use mutable
default arguments and expect them to have specific values.

> I suspect those same people (unless they are experts in the murky corners of
> the language) would expect:
>
>   x=fn()
>
> when fn specifies a default argument of [], to be the same as writing:
>
>   x=fn([])
>
> and not be dependent on fn's entire call history.

Tell me, do you expect these to do the same thing?

x = []
fn(x)
fn(x)
fn(x)

# or

fn([])
fn([])
fn([])

The distinction is exactly the same. If you can understand that the
first one constructs a single object and uses it three times, then you
should be able to understand that the function default is also
constructed once and used every time.

> Your solution of using:
>
>   def fn(a=None):
>
> is not really satisfactory. Partly because it now utilities two mechanisms
> for the default: first to get None, then some extra code to get []. But also
> it's no longer obvious how the function works and what the default is. At
> least, you can't tell from a glance at the start of the function that [] is
> the default.

This I do agree with. It's a bit clunky. It would be nice to be able to say:

def fn(a=`[]`):
    """If a is not passed, it will be a new empty list"""

and have it function the same as:

def fn(*args):
    a = args[0] if args else []

but currently there's no mechanism for it. (The example spelling I
gave is almost certainly not going to be used, due to the Tim Peters'
Monitor Grit argument, but something could probably be found.) It's a
sufficiently common situation that it would merit its own syntax. Want
to write up a PEP? All you need is a syntax that people can get
behind.

ChrisA



More information about the Python-list mailing list