Differences between revisions 1 and 16 (spanning 15 versions)
Revision 1 as of 2005-09-03 01:32:31
Size: 281
Editor: NickCoghlan
Comment:
Revision 16 as of 2005-09-03 05:27:20
Size: 5211
Editor: NickCoghlan
Comment: Add print builtin to 'no trailing newline' example
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 an 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.

=== 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 `lnterm` 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.

Some names which have been suggested are:
 * `print` - excellent name, but causes severe transition problems as described above
 * `println` - avoids the transition problems, reflects default behaviour of adding a line, matches Java method name
 * `printline` - similar to `println`, but avoids the somewhat cryptic abbreviation
 * `out` - not a verb, and converting to it may be problematic due to shadowing by variable names
 * `output` - nice symmetry with input, but using the term as a verb is not typical

An alternative transition strategy would be to add `println` during the Python 2.x series, then, in Py3k, remove the `print` statement and replace it with a `print` builtin which did not append a trailing line separator, and did not support the `lnterm` keyword. The use of `println` with `lnterm = ''` would then simply be a transition strategy - normal Py3k usage would be to use `print` if you didn't want the newline added, and `println` if you did. The `lnterm` argument to `println` would typically only be used to get a different line separator, rather than to eliminate the line separator entirely.

This technique of having two methods is not uncommon - Java has both `print` and `println` methods, and C# has `Write` and `WriteLine`.

=== Sample implementation ===
This is a Python 2.4 compatible sample implementation, which is why it uses the name `println` rather than `print`.

{{{#!python
def println(*args, **kwds):
    """Functional replacement for the print statement

    >>> println(1, 2, 3)
    1 2 3
    >>> println(1, 2, 3, sep='')
    123
    >>> println(1, 2, 3, sep=', ')
    1, 2, 3
    >>> println(1, 2, 3, lnterm='Alternate line terminator\n')
    1 2 3Alternate line terminator
    >>> import sys
    >>> println(1, 2, 3, stream=sys.stderr)
    1 2 3
    >>> println(*range(10))
    0 1 2 3 4 5 6 7 8 9
    >>> println(*(x*x for x in range(10)))
    0 1 4 9 16 25 36 49 64 81
    """
    # Parse the keyword-only optional arguments
    defaults = {
        "sep": " ",
        "lnterm": "\n",
        "stream": sys.stdout,
    }
    for name, default in defaults.items():
        item = None
        try:
            item = kwds[name]
        except KeyError:
            pass
        if item is None:
            kwds[name] = default
    sep, lnterm, stream = kwds["sep"], kwds["lnterm"], kwds["stream"]
    # Perform the print operation without building the whole string
    for arg in args[:1]:
        stream.write(str(arg))
    for arg in args[1:]:
        stream.write(sep)
        stream.write(str(arg))
    stream.write(lnterm)
}}}

=== Code comparisons ===
These are some comparisons of current print statements with the equivalent code using the builtin.

'''Standard printing:'''
{{{#!python
print 1, 2, 3
println(1, 2, 3)
}}}

'''Printing without any spaces:'''
{{{#!python
print "%d%d%d" % (1, 2, 3)
println(1, 2, 3, sep='')
}}}

'''Print as comma separated list:'''
{{{#!python
print "%d, %d%, d" % (1, 2, 3)
println(1, 2, 3, sep=', ')
}}}

'''Print without a trailing newline:'''
{{{#!python
print 1, 2, 3,
println(1, 2, 3, lnterm='')
print(1, 2, 3) # If Py3k gains a print builtin to replace the print statement
}}}

'''Print to a different stream:'''
{{{#!python
print >> sys.stderr, 1, 2, 3
println(1, 2, 3, stream=sys.stderr)
}}}

'''Print a simple sequence:'''
{{{#!python
print " ".join(map(str, range(10)))
println(*range(10))
}}}

'''Print a generator expression:'''
{{{#!python
print " ".join(str(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 lnterm 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.

Some names which have been suggested are:

  • print - excellent name, but causes severe transition problems as described above

  • println - avoids the transition problems, reflects default behaviour of adding a line, matches Java method name

  • printline - similar to println, but avoids the somewhat cryptic abbreviation

  • out - not a verb, and converting to it may be problematic due to shadowing by variable names

  • output - nice symmetry with input, but using the term as a verb is not typical

An alternative transition strategy would be to add println during the Python 2.x series, then, in Py3k, remove the print statement and replace it with a print builtin which did not append a trailing line separator, and did not support the lnterm keyword. The use of println with lnterm = '' would then simply be a transition strategy - normal Py3k usage would be to use print if you didn't want the newline added, and println if you did. The lnterm argument to println would typically only be used to get a different line separator, rather than to eliminate the line separator entirely.

This technique of having two methods is not uncommon - Java has both print and println methods, and C# has Write and WriteLine.

Sample implementation

This is a Python 2.4 compatible sample implementation, which is why it uses the name println rather than print.

   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, lnterm='Alternate line terminator\n')
  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         "lnterm": "\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, lnterm, stream = kwds["sep"], kwds["lnterm"], 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(lnterm)

Code comparisons

These are some comparisons of current print statements with the equivalent code using the builtin.

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, lnterm='')
   3 print(1, 2, 3) # If Py3k gains a print builtin to replace the print statement

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.