3

Suppose we have the following dummy function:

import sys

def writeline(text, stream=sys.stdout):
    stream.write(text + '\n')

with open('/path/to/file', 'w') as f:
    # writes to /path/to/file
    writeline('foo', f)

# writes to standard output
writeline('bar')

Given that Python evaluates default arguments for functions at definition time, is setting sys.stdout as default argument safe, or may it have unintended side effects?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
farsil
  • 955
  • 6
  • 19
  • 1
    Unless you'll [temporarily redirect `stdout` within your client code](http://stackoverflow.com/questions/6796492/temporarily-redirect-stdout-stderr), you should be fine. If you want to do this redirection, function would still hold a reference to original stdout, therefore redirection will not work. – Łukasz Rogalski Mar 13 '17 at 11:11

3 Answers3

5

A problem I think of, is that you sometimes want to redirect sys.stdout to a file (or pipe, device, etc.) yourself.

For instance your main program could look like:

if __name__ == '__main__':
    if len(sys.argv) > 1:
        sys.stdout = open(sys.argv[1],'w')
    try:
        # ... run the program
    finally:
        if len(sys.argv) > 1:
            sys.stdout.close()

This could be useful if you want your program to log to a file if you mention one (like python3 file.py logfile.log). Now since you set the sys.stdout, that modification will not be noted by your writeline method.

Therefore I think it is more safe to write:

def writeline(text, stream = None):
    if stream is None:
        stream = sys.stdout
    stream.write(text + '\n')

In general it is probably good advice to set immutable objects as default parameters (like None, False, (1,), etc.). Only in rare circumstances immutable ones (or ones that might change reference) are used on purpose in Python.

If you are however sure that you will not redirect sys.stdout to a file, pipe, etc. It is safe.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Good point. I am aware of the reason it is better to set _immutable_ objects as default parameters, but I thought it wasn't necessary in this case because I didn't consider the possibility that `sys.stdout` itself is allowed to change. – farsil Mar 13 '17 at 13:02
1

The idiom most frequently used when there are doubts about the constant value of a named argument is to set the argument to None, so the resolution is always done at runtime against the values that are current at invocation time:

def writeline(text, stream=None):
    if stream is None:
        stream = sys.stdout
    stream.write(text + '\n')
Apalala
  • 9,017
  • 3
  • 30
  • 48
0

If you're using Python 3's print function, if the file argument is None output will be printed to stdout as usual. So you can just make the default value None like so:

def write_something(items, outfile=None):
    for item in items:
        print(item, file=outfile)
polm23
  • 14,456
  • 7
  • 35
  • 59