Jeff wrote:

Is this avoidable by using a call to list() in the definition instead?

No. Default values are *always* evaluated when, and only when, the "def" statement is executed; see:

    http://docs.python.org/ref/function.html

Also note that "def" is an executable statement in Python, and that default arguments are evaluated in the "def" statement's environment. If you execute "def" multiple times, it'll create a new function object (with freshly calculated default values) each time.

:::

The workaround is, as others have mentioned, to use a placeholder value instead of modifying the default value. None is a common value:

    def myfunc(value=None):
        if value is None:
            value = default()
        # go on an modify value

If you need to handle arbitrary objects (including None), you can use a sentinel object:

    sentinel = object()

    def myfunc(value=sentinel):
        if value is sentinel:
            value = default()

(in older code, written before "object" was introduced, you sometimes see things like "sentinel = ['placeholder']" used to create a non-false object with a unique identity; [] creates a new list every time it is evaluated.)

:::

Finally, it should be noted that more advanced Python code often uses this mechanism to its advantage; for example, if you create a bunch of UI buttons in a loop, you might try something like:

     for i in range(10):
         def callback():
             print "clicked button", i
         UI.Button("button %s" % i, callback)

only to find that all callbacks print the same value (most likely 9, in this case). The reason for this is that Python's nested scopes bind to variables, not object values, so all callback instances will see the current (=last) value of the "i" variable. To fix this, use explicit binding:

     for i in range(10):
         def callback(i=i):
             print "clicked button", i
         UI.Button("button %s" % i, callback)

The "i=i" part binds a local variable "i" to the *current* value of the outer variable "i".

Two other uses are local caches/memoization; e.g.

    def calculate(a, b, c, memo={}):
        try:
            value = memo[a, b, c] # return already calculated value
        except KeyError:
            value = do calculation on a, b, c
            memo[a, b, c] = value # update the memo dictionary
        return value

(this is especially nice for certain kinds of recursive algorithms)

and, for highly optimized code, local rebinding of global names:

    def this_one_must_be_fast(x, sin=math.sin, cos=math.cos):
        ...

Hope this helps more than it confuses.

</F>

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to