2000-07-24 13:38:35 -04:00
|
|
|
PEP: 214
|
|
|
|
Title: Extended Print Statement
|
|
|
|
Version: $Revision$
|
2006-03-23 15:13:19 -05:00
|
|
|
Last-Modified: $Date$
|
2007-06-28 15:53:41 -04:00
|
|
|
Author: barry@python.org (Barry Warsaw)
|
2000-09-23 04:19:29 -04:00
|
|
|
Status: Final
|
2007-06-19 00:20:07 -04:00
|
|
|
Type: Standards Track
|
2017-08-11 11:35:05 -04:00
|
|
|
Content-Type: text/x-rst
|
2000-08-15 18:45:06 -04:00
|
|
|
Created: 24-Jul-2000
|
2007-06-19 00:20:07 -04:00
|
|
|
Python-Version: 2.0
|
2000-08-16 10:59:57 -04:00
|
|
|
Post-History: 16-Aug-2000
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
|
|
|
Introduction
|
2017-08-11 11:35:05 -04:00
|
|
|
============
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
This PEP describes a syntax to extend the standard 'print'
|
|
|
|
statement so that it can be used to print to any file-like object,
|
|
|
|
instead of the default ``sys.stdout``. This PEP tracks the status and
|
|
|
|
ownership of this feature. It contains a description of the
|
|
|
|
feature and outlines changes necessary to support the feature.
|
|
|
|
This PEP summarizes discussions held in mailing list forums, and
|
|
|
|
provides URLs for further information, where appropriate. The CVS
|
|
|
|
revision history of this file contains the definitive historical
|
|
|
|
record.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
2000-08-16 10:59:57 -04:00
|
|
|
Proposal
|
2017-08-11 11:35:05 -04:00
|
|
|
========
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
This proposal introduces a syntax extension to the print
|
|
|
|
statement, which allows the programmer to optionally specify the
|
|
|
|
output file target. An example usage is as follows::
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print >> mylogfile, 'this message goes to my log file'
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
Formally, the syntax of the extended print statement is::
|
2017-03-24 17:11:33 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print_stmt: ... | '>>' test [ (',' test)+ [','] ] )
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
where the ellipsis indicates the original print_stmt syntax
|
|
|
|
unchanged. In the extended form, the expression just after >>
|
|
|
|
must yield an object with a ``write()`` method (i.e. a file-like
|
|
|
|
object). Thus these two statements are equivalent::
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print 'hello world'
|
|
|
|
print >> sys.stdout, 'hello world'
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
As are these two statements::
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print
|
|
|
|
print >> sys.stdout
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
These two statements are syntax errors::
|
2000-08-16 10:59:57 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print ,
|
|
|
|
print >> sys.stdout,
|
2000-08-16 10:59:57 -04:00
|
|
|
|
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
Justification
|
2017-08-11 11:35:05 -04:00
|
|
|
=============
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
'print' is a Python keyword and introduces the print statement as
|
|
|
|
described in section 6.6 of the language reference manual [1]_.
|
|
|
|
The print statement has a number of features:
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
- it auto-converts the items to strings
|
|
|
|
- it inserts spaces between items automatically
|
|
|
|
- it appends a newline unless the statement ends in a comma
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
The formatting that the print statement performs is limited; for
|
|
|
|
more control over the output, a combination of ``sys.stdout.write()``,
|
|
|
|
and string interpolation can be used.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
The print statement by definition outputs to ``sys.stdout``. More
|
|
|
|
specifically, ``sys.stdout`` must be a file-like object with a ``write()``
|
|
|
|
method, but it can be rebound to redirect output to files other
|
|
|
|
than specifically standard output. A typical idiom is::
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
save_stdout = sys.stdout
|
|
|
|
try:
|
|
|
|
sys.stdout = mylogfile
|
|
|
|
print 'this message goes to my log file'
|
|
|
|
finally:
|
|
|
|
sys.stdout = save_stdout
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
The problem with this approach is that the binding is global, and
|
|
|
|
so affects every statement inside the try: clause. For example,
|
|
|
|
if we added a call to a function that actually did want to print
|
|
|
|
to stdout, this output too would get redirected to the logfile.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
This approach is also very inconvenient for interleaving prints to
|
|
|
|
various output streams, and complicates coding in the face of
|
|
|
|
legitimate try/except or try/finally clauses.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2000-08-15 18:45:06 -04:00
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
Reference Implementation
|
2017-08-11 11:35:05 -04:00
|
|
|
========================
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
A reference implementation, in the form of a patch against the
|
|
|
|
Python 2.0 source tree, is available on SourceForge's patch
|
|
|
|
manager [2]_. This approach adds two new opcodes, ``PRINT_ITEM_TO`` and
|
|
|
|
``PRINT_NEWLINE_TO``, which simply pop the file like object off the
|
|
|
|
top of the stack and use it instead of ``sys.stdout`` as the output
|
|
|
|
stream.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
(This reference implementation has been adopted in Python 2.0.)
|
2000-10-27 06:25:44 -04:00
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
Alternative Approaches
|
2017-08-11 11:35:05 -04:00
|
|
|
======================
|
|
|
|
|
|
|
|
An alternative to this syntax change has been proposed (originally
|
|
|
|
by Moshe Zadka) which requires no syntax changes to Python. A
|
|
|
|
``writeln()`` function could be provided (possibly as a builtin), that
|
|
|
|
would act much like extended print, with a few additional
|
|
|
|
features::
|
|
|
|
|
|
|
|
def writeln(*args, **kws):
|
|
|
|
import sys
|
|
|
|
file = sys.stdout
|
|
|
|
sep = ' '
|
|
|
|
end = '\n'
|
|
|
|
if kws.has_key('file'):
|
|
|
|
file = kws['file']
|
|
|
|
del kws['file']
|
|
|
|
if kws.has_key('nl'):
|
|
|
|
if not kws['nl']:
|
|
|
|
end = ' '
|
|
|
|
del kws['nl']
|
|
|
|
if kws.has_key('sep'):
|
|
|
|
sep = kws['sep']
|
|
|
|
del kws['sep']
|
|
|
|
if kws:
|
|
|
|
raise TypeError('unexpected keywords')
|
|
|
|
file.write(sep.join(map(str, args)) + end)
|
|
|
|
|
|
|
|
``writeln()`` takes a three optional keyword arguments. In the
|
|
|
|
context of this proposal, the relevant argument is 'file' which
|
|
|
|
can be set to a file-like object with a ``write()`` method. Thus::
|
|
|
|
|
|
|
|
print >> mylogfile, 'this goes to my log file'
|
|
|
|
|
|
|
|
would be written as::
|
|
|
|
|
|
|
|
writeln('this goes to my log file', file=mylogfile)
|
|
|
|
|
|
|
|
``writeln()`` has the additional functionality that the keyword
|
|
|
|
argument 'nl' is a flag specifying whether to append a newline or
|
|
|
|
not, and an argument 'sep' which specifies the separator to output
|
|
|
|
in between each item.
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
2000-08-25 10:15:49 -04:00
|
|
|
More Justification by the BDFL
|
2017-08-11 11:35:05 -04:00
|
|
|
==============================
|
|
|
|
|
|
|
|
The proposal has been challenged on the newsgroup. One series of
|
|
|
|
challenges doesn't like '>>' and would rather see some other
|
|
|
|
symbol.
|
|
|
|
|
|
|
|
* Challenge: Why not one of these?
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
print in stderr items,....
|
|
|
|
print + stderr items,.......
|
|
|
|
print[stderr] items,.....
|
|
|
|
print to stderr items,.....
|
|
|
|
|
|
|
|
Response: If we want to use a special symbol (``print <symbol>``
|
|
|
|
expression), the Python parser requires that it is not already a
|
|
|
|
symbol that can start an expression -- otherwise it can't decide
|
|
|
|
which form of print statement is used. (The Python parser is a
|
|
|
|
simple LL(1) or recursive descent parser.)
|
|
|
|
|
|
|
|
This means that we can't use the "keyword only in context trick"
|
|
|
|
that was used for "import as", because an identifier can start an
|
|
|
|
expression. This rules out +stderr, \[sterr\], and to stderr. It
|
|
|
|
leaves us with binary operator symbols and other miscellaneous
|
|
|
|
symbols that are currently illegal here, such as 'import'.
|
|
|
|
|
|
|
|
If I had to choose between 'print in file' and 'print >> file' I
|
|
|
|
would definitely choose '>>'. In part because 'in' would be a new
|
|
|
|
invention (I know of no other language that uses it, while '>>' is
|
|
|
|
used in sh, awk, Perl, and C++), in part because '>>', being
|
|
|
|
non-alphabetic, stands out more so is more likely to catch the
|
|
|
|
reader's attention.
|
|
|
|
|
|
|
|
* Challenge: Why does there have to be a comma between the file and
|
|
|
|
the rest?
|
|
|
|
|
|
|
|
Response: The comma separating the file from the following expression is
|
|
|
|
necessary! Of course you want the file to be an arbitrary
|
|
|
|
expression, not just a single word. (You definitely want to be
|
|
|
|
able to write ``print >>sys.stderr``.) Without the expression the
|
|
|
|
parser would't be able to distinguish where that expression ends
|
|
|
|
and where the next one begins, e.g.
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
print >>i +1, 2
|
|
|
|
print >>a [1], 2
|
|
|
|
print >>f (1), 2
|
|
|
|
|
|
|
|
* Challenge: Why do you need a syntax extension? Why not
|
|
|
|
writeln(file, item, ...)?
|
|
|
|
|
|
|
|
Response: First of all, this is lacking a feature of the print
|
|
|
|
statement: the trailing comma to print which suppresses the final
|
|
|
|
newline. Note that 'print a,' still isn't equivalent to
|
|
|
|
'sys.stdout.write(a)' -- print inserts a space between items, and
|
|
|
|
takes arbitrary objects as arguments; ``write()`` doesn't insert a
|
|
|
|
space and requires a single string.
|
|
|
|
|
|
|
|
When you are considering an extension for the print statement,
|
|
|
|
it's not right to add a function or method that adds a new feature
|
|
|
|
in one dimension (where the output goes) but takes away in another
|
|
|
|
dimension (spaces between items, and the choice of trailing
|
|
|
|
newline or not). We could add a whole slew of methods or
|
|
|
|
functions to deal with the various cases but that seems to add
|
|
|
|
more confusion than necessary, and would only make sense if we
|
|
|
|
were to deprecate the print statement altogether.
|
|
|
|
|
|
|
|
I feel that this debate is really about whether print should have
|
|
|
|
been a function or method rather than a statement. If you are in
|
|
|
|
the function camp, of course adding special syntax to the existing
|
|
|
|
print statement is not something you like. I suspect the
|
|
|
|
objection to the new syntax comes mostly from people who already
|
|
|
|
think that the print statement was a bad idea. Am I right?
|
|
|
|
|
|
|
|
About 10 years ago I debated with myself whether to make the most
|
|
|
|
basic from of output a function or a statement; basically I was
|
|
|
|
trying to decide between "print(item, ...)" and "print item, ...".
|
|
|
|
I chose to make it a statement because printing needs to be taught
|
|
|
|
very early on, and is very important in the programs that
|
|
|
|
beginners write. Also, because ABC, which lead the way for so
|
|
|
|
many things, made it a statement. In a move that's typical for
|
|
|
|
the interaction between ABC and Python, I changed the name from
|
|
|
|
WRITE to print, and reversed the convention for adding newlines
|
|
|
|
from requiring extra syntax to add a newline (ABC used trailing
|
|
|
|
slashes to indicate newlines) to requiring extra syntax (the
|
|
|
|
trailing comma) to suppress the newline. I kept the feature that
|
|
|
|
items are separated by whitespace on output.
|
|
|
|
|
|
|
|
Full example: in ABC,
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
WRITE 1
|
|
|
|
WRITE 2/
|
|
|
|
|
|
|
|
has the same effect as::
|
|
|
|
|
|
|
|
print 1,
|
|
|
|
print 2
|
|
|
|
|
|
|
|
has in Python, outputting in effect "1 2\n".
|
|
|
|
|
|
|
|
I'm not 100% sure that the choice for a statement was right (ABC
|
|
|
|
had the compelling reason that it used statement syntax for
|
|
|
|
anything with side effects, but Python doesn't have this
|
|
|
|
convention), but I'm also not convinced that it's wrong. I
|
|
|
|
certainly like the economy of the print statement. (I'm a rabid
|
|
|
|
Lisp-hater -- syntax-wise, not semantics-wise! -- and excessive
|
|
|
|
parentheses in syntax annoy me. Don't ever write ``return(i) or
|
|
|
|
if(x==y):`` in your Python code! :-)
|
|
|
|
|
|
|
|
Anyway, I'm not ready to deprecate the print statement, and over
|
|
|
|
the years we've had many requests for an option to specify the
|
|
|
|
file.
|
|
|
|
|
|
|
|
* Challenge: Why not > instead of >>?
|
|
|
|
|
|
|
|
Response: To DOS and Unix users, >> suggests "append", while >
|
|
|
|
suggests "overwrite"; the semantics are closest to append. Also,
|
|
|
|
for C++ programmers, >> and << are I/O operators.
|
|
|
|
|
|
|
|
* Challenge: But in C++, >> is input and << is output!
|
|
|
|
|
|
|
|
Response: doesn't matter; C++ clearly took it from Unix and
|
|
|
|
reversed the arrows. The important thing is that for output, the
|
|
|
|
arrow points to the file.
|
|
|
|
|
|
|
|
* Challenge: Surely you can design a ``println()`` function can do all
|
|
|
|
what ``print>>file`` can do; why isn't that enough?
|
|
|
|
|
|
|
|
Response: I think of this in terms of a simple programming
|
|
|
|
exercise. Suppose a beginning programmer is asked to write a
|
|
|
|
function that prints the tables of multiplication. A reasonable
|
|
|
|
solution is::
|
|
|
|
|
|
|
|
def tables(n):
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
print i, 'x', j, '=', i*j
|
|
|
|
print
|
|
|
|
|
|
|
|
Now suppose the second exercise is to add printing to a different
|
|
|
|
file. With the new syntax, the programmer only needs to learn one
|
|
|
|
new thing: ``print >> file``, and the answer can be like this::
|
|
|
|
|
|
|
|
def tables(n, file=sys.stdout):
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
print >> file, i, 'x', j, '=', i*j
|
|
|
|
print >> file
|
|
|
|
|
|
|
|
With only a print statement and a ``println()`` function, the
|
|
|
|
programmer first has to learn about ``println()``, transforming the
|
|
|
|
original program to using ``println()``::
|
|
|
|
|
|
|
|
def tables(n):
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
println(i, 'x', j, '=', i*j)
|
|
|
|
println()
|
|
|
|
|
|
|
|
and **then** about the file keyword argument::
|
|
|
|
|
|
|
|
def tables(n, file=sys.stdout):
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
println(i, 'x', j, '=', i*j, file=sys.stdout)
|
|
|
|
println(file=sys.stdout)
|
|
|
|
|
|
|
|
Thus, the transformation path is longer::
|
|
|
|
|
|
|
|
(1) print
|
|
|
|
(2) print >> file
|
2000-08-25 10:15:49 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
vs.
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
::
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
(1) print
|
|
|
|
(2) println()
|
|
|
|
(3) println(file=...)
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
Note: defaulting the file argument to ``sys.stdout`` at compile time
|
|
|
|
is wrong, because it doesn't work right when the caller assigns to
|
|
|
|
``sys.stdout`` and then uses ``tables()`` without specifying the file.
|
|
|
|
This is a common problem (and would occur with a ``println()``
|
|
|
|
function too). The standard solution so far has been::
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
def tables(n, file=None):
|
|
|
|
if file is None:
|
|
|
|
file = sys.stdout
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
print >> file, i, 'x', j, '=', i*j
|
|
|
|
print >> file
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
I've added a feature to the implementation (which I would also
|
|
|
|
recommend to ``println()``) whereby if the file argument is ``None``,
|
|
|
|
``sys.stdout`` is automatically used. Thus,
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
::
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print >> None, foo bar
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
(or, of course, ``print >> x`` where x is a variable whose value is
|
|
|
|
None) means the same as
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
::
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
print foo, bar
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
and the ``tables()`` function can be written as follows::
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
def tables(n, file=None):
|
|
|
|
for j in range(1, n+1):
|
|
|
|
for i in range(1, n+1):
|
|
|
|
print >> file, i, 'x', j, '=', i*j
|
|
|
|
print >> file
|
2000-09-03 11:10:58 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
.. XXX this needs more justification, and a section of its own
|
2000-10-27 06:25:44 -04:00
|
|
|
|
2000-08-25 10:15:49 -04:00
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
References
|
2017-08-11 11:35:05 -04:00
|
|
|
==========
|
2000-07-24 13:38:35 -04:00
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
.. [1] http://docs.python.org/reference/simple_stmts.html#print
|
|
|
|
.. [2] http://sourceforge.net/patch/download.php?id=100970
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
2017-08-11 11:35:05 -04:00
|
|
|
..
|
|
|
|
Local Variables:
|
|
|
|
mode: indented-text
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
End:
|