Differences between revisions 11 and 12
Revision 11 as of 2005-09-03 02:27:14
Size: 3153
Editor: NickCoghlan
Comment: Fix no trailing newline example
Revision 12 as of 2005-09-03 02:42:46
Size: 3946
Editor: NickCoghlan
Comment: Add section on transition issues, change name to println
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
This page discusses the benefits of replacing the current print statement with an equivalent builtin. The output function presented below does everything the print statement does without requiring any hacking of the grammar, and also makes a number of things significantly easier. This page discusses the benefits of replacing the current `print` statement with an equivalent builtin. The `println` function presented below does everything the `print` statement does without requiring any hacking of the grammar, and also makes a number of things significantly easier.
Line 11: Line 11:
=== Getting there from here ===
The example implementation below shows that creating a function with the desired behaviour is quite straightforward. A problem only arises if we decide we want the builtin to have the name `print`. This seriously complicates transition, because `print` is a reserved word in Python 2.x. Since the `print` statement will be around until Py3K allows us to break backwards compatibility, devising a transition plan that lets programmers 'get ready early' for the Py3K transition becomes a significant challenge.

If, on the other hand, the builtin had a different name (such as `println`), it would be quite feasible to introduce it during the 2.x series, with the only change in Py3K being the final removal of the `print` statement.
Line 12: Line 17:
This is a Python 2.4 compatible sample implementation, which is why it uses the name `output` rather than `print`. Try not to get too hung up on names at this stage :) This is a Python 2.4 compatible sample implementation, which is why it uses the name `println` rather than `print`. Try not to get too hung up on names at this stage :)
Line 15: Line 20:
def output(*args, **kwds): def println(*args, **kwds):
Line 18: Line 23:
    >>> output(1, 2, 3)     >>> println(1, 2, 3)
Line 20: Line 25:
    >>> output(1, 2, 3, sep='')     >>> println(1, 2, 3, sep='')
Line 22: Line 27:
    >>> output(1, 2, 3, sep=', ')     >>> println(1, 2, 3, sep=', ')
Line 24: Line 29:
    >>> output(1, 2, 3, term='Alternate line terminator')     >>> println(1, 2, 3, term='Alternate line terminator')
Line 27: Line 32:
    >>> output(1, 2, 3, stream=sys.stderr)     >>> println(1, 2, 3, stream=sys.stderr)
Line 29: Line 34:
    >>> output(*range(10))     >>> println(*range(10))
Line 31: Line 36:
    >>> output(*(x*x for x in range(10)))     >>> println(*(x*x for x in range(10)))
Line 64: Line 69:
output(1, 2, 3) println(1, 2, 3)
Line 70: Line 75:
output(1, 2, 3, sep='') println(1, 2, 3, sep='')
Line 76: Line 81:
output(1, 2, 3, sep=', ') println(1, 2, 3, sep=', ')
Line 82: Line 87:
output(1, 2, 3, term='') println(1, 2, 3, term='')
Line 88: Line 93:
output(1, 2, 3, stream=sys.stderr) println(1, 2, 3, stream=sys.stderr)
Line 94: Line 99:
output(*range(10)) println(*range(10))
Line 100: Line 105:
output(*(x*x for x in range(10))) println(*(x*x for x in range(10)))

This page discusses the benefits of replacing the current print statement with an equivalent builtin. The println function presented below does everything the print statement does without requiring any hacking of the grammar, and also makes a number of things significantly easier.

Benefits of using a function instead of a statement

  • Extended call syntax provides better interaction with sequences
  • Keyword argument sep allows separator to be changed easily and obviously

  • Keyword argument term allows line terminator to be changed easily and obviously

  • Keyword argument stream allows easy and obvious redirection

  • The builtin can be replaced for application wide customisation (e.g. per-thread logging)
  • Interacts well with PEP 309's partial function application

Getting there from here

The example implementation below shows that creating a function with the desired behaviour is quite straightforward. A problem only arises if we decide we want the builtin to have the name print. This seriously complicates transition, because print is a reserved word in Python 2.x. Since the print statement will be around until Py3K allows us to break backwards compatibility, devising a transition plan that lets programmers 'get ready early' for the Py3K transition becomes a significant challenge.

If, on the other hand, the builtin had a different name (such as println), it would be quite feasible to introduce it during the 2.x series, with the only change in Py3K being the final removal of the print statement.

Sample implementation

This is a Python 2.4 compatible sample implementation, which is why it uses the name println rather than print. Try not to get too hung up on names at this stage :)

   1 def println(*args, **kwds):
   2     """Functional replacement for the print statement
   3 
   4     >>> println(1, 2, 3)
   5     1 2 3
   6     >>> println(1, 2, 3, sep='')
   7     123
   8     >>> println(1, 2, 3, sep=', ')
   9     1, 2, 3
  10     >>> println(1, 2, 3, term='Alternate line terminator')
  11     1 2 3Alternate line terminator
  12     >>> import sys
  13     >>> println(1, 2, 3, stream=sys.stderr)
  14     1 2 3
  15     >>> println(*range(10))
  16     0 1 2 3 4 5 6 7 8 9
  17     >>> println(*(x*x for x in range(10)))
  18     0 1 4 9 16 25 36 49 64 81
  19     """
  20     # Parse the keyword-only optional arguments
  21     defaults = {
  22         "sep": " ",
  23         "term": "\n",
  24         "stream": sys.stdout,
  25     }
  26     for name, default in defaults.items():
  27         item = None
  28         try:
  29             item = kwds[name]
  30         except KeyError:
  31             pass
  32         if item is None:
  33             kwds[name] = default
  34     sep, term, stream = kwds["sep"], kwds["term"], kwds["stream"]
  35     # Perform the print operation without building the whole string
  36     for arg in args[:1]:
  37         stream.write(str(arg))
  38     for arg in args[1:]:
  39         stream.write(sep)
  40         stream.write(str(arg))
  41     stream.write(term)

Code comparisons

These are some comparisons of current print statements with the equivalent code using the builtin (again, don't get too hung up on names here).

Standard printing:

   1 print 1, 2, 3
   2 println(1, 2, 3)

Printing without any spaces:

   1 print "%d%d%d" % (1, 2, 3)
   2 println(1, 2, 3, sep='')

Print as comma separated list:

   1 print "%d, %d%, d" % (1, 2, 3)
   2 println(1, 2, 3, sep=', ')

Print without a trailing newline:

   1 print 1, 2, 3,
   2 println(1, 2, 3, term='')

Print to a different stream:

   1 print >> sys.stderr, 1, 2, 3
   2 println(1, 2, 3, stream=sys.stderr)

Print a simple sequence:

   1 print " ".join(map(str, range(10)))
   2 println(*range(10))

Print a generator expression:

   1 print " ".join(str(x*x) for x in range(10))
   2 println(*(x*x for x in range(10)))

PrintAsFunction (last edited 2011-08-14 09:25:04 by eth595)

Unable to edit the page? See the FrontPage for instructions.