2000-07-24 13:38:35 -04:00
|
|
|
|
PEP: 214
|
|
|
|
|
Title: Extended Print Statement
|
|
|
|
|
Version: $Revision$
|
2000-10-30 16:16:38 -05:00
|
|
|
|
Author: barry@digicool.com (Barry A. Warsaw)
|
2000-08-21 09:36:22 -04:00
|
|
|
|
Python-Version: 2.0
|
2000-09-23 04:19:29 -04:00
|
|
|
|
Status: Final
|
2000-08-15 18:45:06 -04:00
|
|
|
|
Created: 24-Jul-2000
|
2000-08-16 10:59:57 -04:00
|
|
|
|
Post-History: 16-Aug-2000
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Introduction
|
|
|
|
|
|
|
|
|
|
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-08-16 10:59:57 -04:00
|
|
|
|
Proposal
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
print >> mylogfile, 'this message goes to my log file'
|
|
|
|
|
|
|
|
|
|
Formally, the syntax of the extended print statement is
|
|
|
|
|
|
|
|
|
|
print_stmt: ... | '>>' test [ (',' test)+ [','] ] )
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
print 'hello world'
|
|
|
|
|
print >> sys.stdout, 'hello world'
|
|
|
|
|
|
|
|
|
|
As are these two statements:
|
|
|
|
|
|
|
|
|
|
print
|
|
|
|
|
print >> sys.stdout
|
|
|
|
|
|
|
|
|
|
These two statements are syntax errors:
|
|
|
|
|
|
|
|
|
|
print ,
|
|
|
|
|
print >> sys.stdout,
|
|
|
|
|
|
|
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
|
Justification
|
|
|
|
|
|
|
|
|
|
`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:
|
|
|
|
|
|
|
|
|
|
- it auto-converts the items to strings
|
|
|
|
|
- it inserts spaces between items automatically
|
|
|
|
|
- it appends a newline unless the statement ends in a comma
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
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-10-27 06:25:44 -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
|
|
|
|
|
|
|
|
|
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-08-15 18:45:06 -04:00
|
|
|
|
This approach is also very inconvenient for interleaving prints to
|
2000-08-16 10:59:57 -04:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
A reference implementation, in the form of a patch against the
|
|
|
|
|
Python 2.0 source tree, is available on SourceForge's patch
|
2000-08-15 18:45:06 -04:00
|
|
|
|
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
|
|
|
|
|
2000-10-27 06:25:44 -04:00
|
|
|
|
(This reference implementation has been adopted in Python 2.0.)
|
|
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
|
|
|
|
|
|
Alternative Approaches
|
|
|
|
|
|
|
|
|
|
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-08-25 10:15:49 -04:00
|
|
|
|
More Justification by the BDFL
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2000-09-03 11:10:58 -04:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
vs.
|
|
|
|
|
|
|
|
|
|
(1) print
|
|
|
|
|
(2) println()
|
|
|
|
|
(3) println(file=...)
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
|
|
|
|
print >> None, foo bar
|
|
|
|
|
|
|
|
|
|
(or, of course, print >> x where x is a variable whose value is
|
|
|
|
|
None) means the same as
|
|
|
|
|
|
|
|
|
|
print foo, bar
|
|
|
|
|
|
|
|
|
|
and the tables() function can be written as follows:
|
|
|
|
|
|
|
|
|
|
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-10-27 06:25:44 -04:00
|
|
|
|
[XXX this needs more justification, and a section of its own]
|
|
|
|
|
|
2000-08-25 10:15:49 -04:00
|
|
|
|
|
2000-07-24 13:38:35 -04:00
|
|
|
|
References
|
|
|
|
|
|
|
|
|
|
[1] http://www.python.org/doc/current/ref/print.html
|
|
|
|
|
[2] http://sourceforge.net/patch/download.php?id=100970
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
End:
|