functions, optional parameters

Steven D'Aprano steve+comp.lang.python at pearwood.info
Fri May 8 11:24:00 EDT 2015


On Fri, 8 May 2015 09:59 pm, Michael Welle 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.

I think it is perfectly expected.

Do you think that Python will re-compile the body of the function every time
you call it? Setting the default is part of the process of compiling the
function.


If we have this function definition:

    def spam(eggs=long_complex_calculation()):
        pass


do you expect the long complex calculation to be repeated every time you
call the function?

How about this definition:

    default = 23
    def spam(eggs=default):
        pass

    del default

    print spam()


Do you expect the function call to fail because `default` doesn't exist?


My answers to those questions are all No. To me, it is not only expected,
but desirable that function defaults are set once, not every time the
function is called. This behaviour is called "early binding" of defaults.

The opposite behaviour is called "late binding".

If your language uses late binding, it is very inconvenient to get early
binding when you want it. But if your language uses early binding, it is
very simple to get late binding when you want it: just put the code you
want to run inside the body of the function:

    # simulate late binding
    def spam(eggs=None):
        if eggs is None:
            # perform the calculation every time you call the function
            eggs = long_complex_calculation()


    default = 23
    def spam(eggs=None):
        if eggs is None:
            # look up the global variable every time you call the function
            eggs = default


On the rare times that you want to allow None as an ordinary value, you can
create your own private sentinel value:


    _SENTINEL = object()

    def spam(eggs=_SENTINEL):
        if eggs is _SENTINEL:
            eggs = something_else



-- 
Steven




More information about the Python-list mailing list