Python bug with [] as default value in functions?

Dinu Gherman gherman at darwin.in-berlin.de
Thu Apr 12 07:59:25 EDT 2001


Hi,

I noticed a very strange behaviour: I've got a recursive func-
tion like this:

  foo(spam, eggs=[]):
    ...
    return foo(spam, eggs=eggs)
    # the following is the same:
    # return foo(spam, eggs)

where the default argument for eggs, the empty list, is *empty* 
for the top-level call *only* when it is explicitly provided 
with the function call like this:

  bar = foo(mySpam, eggs=[])

I've attached a longer example showing, I hope, rather pre-
cisely what I mean.

Is this a bug (I'm using Win2K, Py 2.0) or just a lack of un-
derstanding of recursion and name spaces, delicate as they 
are, in my brain (which is not entirely excluded today...)??

Regards,

Dinu

-- 
Dinu C. Gherman
ReportLab Consultant - http://www.reportlab.com
................................................................
"The only possible values [for quality] are 'excellent' and 'in-
sanely excellent', depending on whether lives are at stake or 
not. Otherwise you don't enjoy your work, you don't work well, 
and the project goes down the drain." 
                    (Kent Beck, "Extreme Programming Explained")
-------------- next part --------------
"""
A test script indicating that empty lists as default values
for function arguments might not work as they should.

The default value [] is not used as defined in the function
signature, except when the function is called indicating this
very value explicitely.

The functions used compute recursively a list of integers
with a sum less than or equal to some given maximum value.

Found on Python 2.0/Win2K.

Dinu Gherman
"""

from operator import add


# [] as default value for L does not seem to work properly!!!

def maxNumList1(Max, L=[]):
    """Find list of consecutive numbers n_i, where sum(n_i) < Max.

    This function computes recursively a list of ordered integers
    n_i, i = 0...m, starting at n_0 = 0, with n_i+1 = n_i + 1
    and sum(n_i) <= Max.

    E.g.
      maxNumList1(0) = [0]
      maxNumList1(1) = [0, 1]
      ..
      maxNumList1(4) = [0, 1, 2]
      ..
      maxNumList1(6) = [0, 1, 2, 3]
      ..
      maxNumList1(9) = [0, 1, 2, 3]
      maxNumList1(10)= [0, 1, 2, 3, 4]
      ..
    """

    assert Max >= 0, "Maximum must be a positive integer."

    # Add initial 0 or next integer.    
    if L == []:
        L.append(0) 
    else:
        L.append(L[-1]+1)

    # Return list if maximum exceeded or recurse.
    if reduce(add, L) > Max:
        return L[:-1]
    else:
        return maxNumList1(Max, L=L)


def maxNumList2(Max, L=None):
    "Same as maxNumList1, but uses None as default value."

    # Hack to fix the bug.
    if L == None:
        L = []

    # From here on identical to maxNumList1() - except for
    # the call to itself at the bottom.

    assert Max >= 0, "Maximum must be a positive integer."

    # Add initial 0 or next integer.    
    if L == []:
        L.append(0) 
    else:
        L.append(L[-1]+1)

    # Return list if maximum exceeded or recurse.
    if reduce(add, L) > Max:
        return L[:-1]
    else:
        return maxNumList2(Max, L=L)


# This is only for reference purposes and does not matter
# or affect the strange behaviour of the implementation
# of maxNumList1().
def maxNumList3(Max):
    "Behaves as maxNumList2, but uses no recursion."
    
    list = []
    i = 0
    while reduce(add, list, 0) <= Max:
        list.append(i)
        i = i + 1
    return list[:-1]


def test():
    print """Testing...
    The second and third lines should show the same results.
    The first should show identical results to the second,
    but doesn't... Bug or no bug??
    """
    for i in range(11):
        n = i
        print "maxNumList1(%d)       = %s" % (n, maxNumList1(n))
        print "maxNumList1(%d, L=[]) = %s" % (n, maxNumList1(n, L=[]))
        print "maxNumList2(%d)       = %s" % (n, maxNumList2(n))
##        print "maxNumList3(%d)       = %s" % (n, maxNumList3(n))
        print


if __name__ == "__main__":
    print __doc__
    test()


More information about the Python-list mailing list