just another default argument value gotcha

Terry Reedy tjreedy at udel.edu
Mon Dec 29 15:11:49 EST 2003


"Frank Bechmann" <fBechmann at web.de> wrote in message
news:dbc00a99.0312290133.3e9e6241 at posting.google.com...
> Eventually most of you will not learn much from this because it's just
> another event in the 'default argument value gotcha' series, but

There are two issues: when the default value is determined and whether it
is mutable.  The concern here is timing.

> I tried to use some method which was documented to write to
> 'sys.stdout' per default

The meaning of the expression 'sys.stdout' may depend on when it is
evaluated.

> so that changing 'sys.stdout' to bind to
> another object should allow to get grip on the method's output.

If you change the binding before the evaulation, you will affect the
result; otherwise not and your inference is incorrect.

> but that didn't work - reason was that that 'sys.stdout' was used as
> default argument value for the method.

Without the doc quoted, I can't tell whether it was misleading or if you
misread it.  If the doc said 'default argument value' then it was exactly
right.

> The following code describes it  better then my english can do,
> so as it seems for me, one should make sure to use the 'f2' variant.

The code to use is the one that gives the result you want.  To give a
function parameter a default *value*, evaluated once and good for all
function calls, write the expression yielding the value (a constant object)
in the definition-time header as in

> import StringIO, sys
>
> def f1(msg, out=sys.stdout):
>     out.write("%s\n" % msg)

To me,  and apparently to GvR, this is what 'default value' means.  If you
want a backup *expression*, conditionally evaluated at each function call
(and therefore potentially yielding different objects in different function
calls), write the expression in the run-time body as in

> def f2(msg, out=None):
>     if not out:
>         out = sys.stdout
>     out.write("%s\n" % msg)

In this case, I see passing an value for param 'out' via the
runtime-evaluated global attribute sys.stdout as an alternate (implicit)
arg passing mechanism, one that is fairly common.  Simplifying f2 gives

def f3(msg):
    out = sys.stdout
    out.write(("%s\n" % msg)

The added complexity of f2 gives one the option of passing 'out' either
directly in the call or indirectly via the global.  I would only use it if
one needed and were going to use the added flexibility.  The authors of the
method you used did not think it necessary.

Another issue your problem touches on is the ambiguity of 'builtin' names
like 'sys' and 'sys.stdout'.  When the docs use such names, they sometimes
to usually to always mean the objects originally bound to the name on
interpreter startup, ignoring any possible later rebindings within a
program execution.  The reader is expected to interprete the text
immediately using the default, pre-startup bindings. For example, in
'functions return None by default', 'None' means the singleton NoneType
object, not its runtime binding.  Similarly, 'sys.stdout' is sometimes used
to refer specifically to its original rather than 'current' binding.  I
would tend to assume this for library modules unless the doc, the code, or
experiment showed otherwise.

Terry J. Reedy






More information about the Python-list mailing list