newbie question on Python tutorial example in section 4.7.1 (default arg values)

Discussion in 'Python' started by Porky Pig Jr, May 1, 2004.

  1. Porky Pig Jr

    Porky Pig Jr Guest

    Hello,
    hope someone can clarify this section for me (or may be there is a
    'newbie FAQs' in which case I would appreciate the link

    Anyway, the example is

    def f(a, L=[]):
    L.append(a)
    return L

    and somehow L just keeps growing between the calls, so

    print f(1)
    returns [1]
    and then
    print f(2)
    returns [1, 2]
    etc. I'm not really clear on this example, but assume that L is set
    aside somewhere (in the function definition), initialized only once
    (during the def), and then all subsequent calls affect that locally
    defined L.

    Given this is how it works (?), I still don't understand how the
    following workaround works:

    def f(a, L=None):
    if L is None:
    L = []
    L.append(a)
    return L

    well, it does work, but why? Seems like we initialize L to None (which
    is as I undersatnd, sort of equivalent of C void), in fact it also
    works if we initialize L to some arbitrary string. What I don't
    understand: we check if
    L is None, and then make it a list and append 'a' to it. So it becomes
    a list. When then on the subsequent call it is None again rather than
    list with a single value of 'a' (in the previous call)?

    TIA
    Porky Pig Jr, May 1, 2004
    #1
    1. Advertising

  2. On 1 May 2004 14:37:44 -0700, (Porky Pig Jr)
    declaimed the following in comp.lang.python:


    > well, it does work, but why? Seems like we initialize L to None (which
    > is as I undersatnd, sort of equivalent of C void), in fact it also


    "void" is a data /type/ (normally applied to pointers, or to
    places where we wish to declare that there is no return from a
    function).

    None is closer to C's null; technically a value that is not any
    type of value.

    > works if we initialize L to some arbitrary string. What I don't
    > understand: we check if
    > L is None, and then make it a list and append 'a' to it. So it becomes
    > a list. When then on the subsequent call it is None again rather than
    > list with a single value of 'a' (in the previous call)?


    The L=None in the argument list is "created" when the
    interpreter processes the def statement, not during later calls.

    L=[] does not change the space created for the "None" during the
    def, it creates a new space -- an empty array -- and attaches the label
    L to that new space. The next time you call, if no second argument is
    supplied, L is reattached to the None space.

    Python is sort of backwards compared to most traditional
    languages. In most languages, a label/variable "L" is associated with a
    "box" (memory address), and assignment to "L" changes the contents of
    the box. Python's labels are "post-it notes", assignment to "L" works by
    moving the post-it label to where-ever the data is located, not by
    moving the data to where L is located.

    --
    > ============================================================== <
    > | Wulfraed Dennis Lee Bieber KD6MOG <
    > | Bestiaria Support Staff <
    > ============================================================== <
    > Home Page: <http://www.dm.net/~wulfraed/> <
    > Overflow Page: <http://wlfraed.home.netcom.com/> <
    Dennis Lee Bieber, May 2, 2004
    #2
    1. Advertising

  3. Porky Pig Jr

    Piet Guest

    Re: newbie answer on Python tutorial example in section 4.7.1 (default arg values)

    (Porky Pig Jr) wrote in message news:<>...
    Hi TIA,
    > def f(a, L=[]):
    > L.append(a)
    > return L
    >
    > and somehow L just keeps growing between the calls, so
    >
    > print f(1)
    > returns [1]
    > and then
    > print f(2)
    > returns [1, 2]
    > etc. I'm not really clear on this example, but assume that L is set
    > aside somewhere (in the function definition), initialized only once
    > (during the def), and then all subsequent calls affect that locally
    > defined L.

    I agree that this one is diffcult to get. The point is (or seems to
    be) that when you do not provide a value for the second argument
    (which is true for all the function calls), the default value is used.
    So during the first call, the interpreter notices that L is lacking,
    assigns the default value to it (empty list) and then passes this
    parameter together with the already given 'a' to the function.
    When you call the function a second time without a value for L, the
    default L will again be passed to the function, but it will not be
    evaluated, so the assignment "L = []" will not be executed and the
    non-empty list L will be passed to the function.
    When you change the function to
    > def f(a, L=None):
    > if L is None:
    > L = []
    > L.append(a)
    > return L

    your first function will evaluate the assignment for L resulting in
    the "value" "None" for L (I am not sure whether one should talk about
    values in this context). On successive calls to the function without a
    given parameter for L, the expression for L will NOT be evaluated, but
    the lack of the parameter will be noted, and thus the if-clause will
    be evaluated as true and assign the value "[]" to L which will then be
    used on further execution.
    This is at least what I thought. I just checked the behaviour of the
    function when you change the default value for L from "None" to "[]".
    In this case I had expected that successive calls of the function
    would notice the absence of L and that the if-clause would evaluate as
    true as well, but this is not the case. So when you change the
    function to
    > def f(a, L=[]):
    > if L is None:
    > L = []
    > L.append(a)
    > return L

    and write
    f(1),f(2) and so on you will also see that the list is growing with
    each function call.
    So my post was not that helpful, I guess. Maybe a python expert has
    some explanation. I am looking forward to see how that thread will
    develop.
    Best wishes
    Piet
    Piet, May 2, 2004
    #3
  4. Porky Pig Jr

    Rich Krauter Guest

    Re: newbie question on Python tutorial example in section 4.7.1(default arg values)

    On Sat, 2004-05-01 at 17:37, Porky Pig Jr wrote:
    > Hello,
    > hope someone can clarify this section for me (or may be there is a
    > 'newbie FAQs' in which case I would appreciate the link
    >
    > Anyway, the example is
    >
    > def f(a, L=[]):
    > L.append(a)
    > return L
    >
    > and somehow L just keeps growing between the calls, so
    >
    > print f(1)
    > returns [1]
    > and then
    > print f(2)
    > returns [1, 2]
    > etc. I'm not really clear on this example, but assume that L is set
    > aside somewhere (in the function definition), initialized only once
    > (during the def), and then all subsequent calls affect that locally
    > defined L.
    >
    > Given this is how it works (?), I still don't understand how the
    > following workaround works:
    >
    > def f(a, L=None):
    > if L is None:
    > L = []
    > L.append(a)
    > return L
    >
    > well, it does work, but why? Seems like we initialize L to None (which
    > is as I undersatnd, sort of equivalent of C void), in fact it also
    > works if we initialize L to some arbitrary string. What I don't
    > understand: we check if
    > L is None, and then make it a list and append 'a' to it. So it becomes
    > a list. When then on the subsequent call it is None again rather than
    > list with a single value of 'a' (in the previous call)?
    >
    > TIA



    Not sure if this will help you, and my description may not be entirely
    accurate, but it helps me to remember that a function is a an object
    with attributes. One of the attributes of a function object is called
    func_defaults, which contains, suprisingly enough, a tuple of function
    defaults:

    (as you said, "L is set aside somewhere")

    >>> def func(a,L=[]):

    L.append(a)
    print L


    >>> dir(func)

    ['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
    '__get__', '__getattribute__', '__hash__', '__init__', '__module__',
    '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults',
    'func_dict', 'func_doc', 'func_globals', 'func_name']

    >>> func.func_defaults

    ([],)
    >>> func(1)

    [1]
    >>> func.func_defaults

    ([1],)
    >>> func(2)

    [1, 2]
    >>> func(3)

    [1, 2, 3]
    >>> func.func_defaults

    ([1, 2, 3],)

    This happens because L (the list at func.fun_defaults[0]) is mutable.
    Every call to func appends a value to that list in its func_defaults
    tuple.

    If L is instead made immutable in the function def, you see different
    behavior:

    >>> def func2(a,L=None):

    if L is None:
    # rebind [] to the name L, but not
    # to func2.func_defaults[0]
    L = []
    L.append(a)
    print L


    >>> func2(1)

    [1]
    >>> func2.func_defaults

    (None,)
    >>> func2(2)

    [2]
    >>> func2.func_defaults

    (None,)
    >>> func2(3,L=[1,2,3])

    # rebind [1,2,3] to the name L, but not to func2.func_defaults[0]
    [1, 2, 3, 3]
    >>> func2.func_defaults

    (None,)

    Hope that helps.

    Rich
    Rich Krauter, May 2, 2004
    #4
  5. Re: newbie question on Python tutorial example in section 4.7.1(defaultarg values)

    Rich Krauter wrote:

    > On Sat, 2004-05-01 at 17:37, Porky Pig Jr wrote:
    >>Anyway, the example is
    >>def f(a, L=[]):
    >> L.append(a)
    >> return L
    >>
    >>I still don't understand how the following workaround works:
    >>
    >>def f(a, L=None):
    >> if L is None:
    >> L = []
    >> L.append(a)
    >> return L
    >>
    >>well, it does work, but why? Seems like we initialize L to None

    Here is the conceptual flaw: We initialize f's default to None.
    When we call f (with only a value for a), on entry to the function
    we associate the name L with the default (None). When the L = []
    line is executed, we re-associate L with a new empty list.

    In the original definition, we set the default to a particular new
    empty list. The default will remain that particular list. L is a
    name that is associated with objects, not a place a value is stored.

    Mr. Pig, hopefully either my explanation or Mr. Krauter's (which is
    also correct) will help you figure this out. The difference between
    the two is our guess at what you don't understand. If it still does
    not make sense, ask again.

    An interesting exercise to show the bit about objects:

    a = []
    b = []
    print id(a), a, id(b), b
    a.append(1)
    print id(a), a, id(b), b
    a = b
    print id(a), a, id(b), b
    a.append(1)
    print id(a), a, id(b), b

    If you can explain the output of the four print statements and are
    still confused, my explanation is not on point.

    -Scott David Daniels
    Scott David Daniels, May 2, 2004
    #5
  6. Porky Pig Jr

    Porky Pig Jr Guest

    Re: newbie question on Python tutorial example in section 4.7.1(default arg values)

    Scott David Daniels <> wrote in message
    > If you can explain the output of the four print statements and are
    > still confused, my explanation is not on point.
    >
    > -Scott David Daniels
    >



    Thanks for all the feedback, I'll study this (and other) example.
    Porky Pig Jr, May 3, 2004
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. mwt
    Replies:
    3
    Views:
    413
    Fuzzyman
    Mar 1, 2006
  2. W. Watson
    Replies:
    13
    Views:
    1,045
    W. Watson
    Sep 20, 2007
  3. n00m
    Replies:
    5
    Views:
    387
  4. Replies:
    7
    Views:
    163
  5. Replies:
    21
    Views:
    275
    Barry Schwarz
    Mar 5, 2014
Loading...

Share This Page