bad recursion, still works
Fredrik Lundh
fredrik at pythonware.com
Wed Jul 16 09:20:23 EDT 2008
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>
More information about the Python-list
mailing list