reSTify PEP 214 (#319)
This commit is contained in:
parent
4dbfaec329
commit
e889fa82a5
544
pep-0214.txt
544
pep-0214.txt
|
@ -5,366 +5,386 @@ Last-Modified: $Date$
|
|||
Author: barry@python.org (Barry Warsaw)
|
||||
Status: Final
|
||||
Type: Standards Track
|
||||
Content-Type: text/x-rst
|
||||
Created: 24-Jul-2000
|
||||
Python-Version: 2.0
|
||||
Post-History: 16-Aug-2000
|
||||
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
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:
|
||||
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'
|
||||
print >> mylogfile, 'this message goes to my log file'
|
||||
|
||||
Formally, the syntax of the extended print statement is
|
||||
Formally, the syntax of the extended print statement is::
|
||||
|
||||
print_stmt: ... | '>>' test [ (',' test)+ [','] ] )
|
||||
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:
|
||||
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'
|
||||
print 'hello world'
|
||||
print >> sys.stdout, 'hello world'
|
||||
|
||||
As are these two statements:
|
||||
As are these two statements::
|
||||
|
||||
print
|
||||
print >> sys.stdout
|
||||
print
|
||||
print >> sys.stdout
|
||||
|
||||
These two statements are syntax errors:
|
||||
These two statements are syntax errors::
|
||||
|
||||
print ,
|
||||
print >> sys.stdout,
|
||||
print ,
|
||||
print >> sys.stdout,
|
||||
|
||||
|
||||
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:
|
||||
'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
|
||||
- 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 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
|
||||
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::
|
||||
|
||||
save_stdout = sys.stdout
|
||||
try:
|
||||
sys.stdout = mylogfile
|
||||
print 'this message goes to my log file'
|
||||
finally:
|
||||
sys.stdout = save_stdout
|
||||
save_stdout = sys.stdout
|
||||
try:
|
||||
sys.stdout = mylogfile
|
||||
print 'this message goes to my log file'
|
||||
finally:
|
||||
sys.stdout = save_stdout
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
Reference Implementation
|
||||
========================
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
(This reference implementation has been adopted in Python 2.0.)
|
||||
(This reference implementation has been adopted in Python 2.0.)
|
||||
|
||||
|
||||
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.
|
||||
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)
|
||||
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
|
||||
``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'
|
||||
print >> mylogfile, 'this goes to my log file'
|
||||
|
||||
would be written as
|
||||
would be written as::
|
||||
|
||||
writeln('this goes to my log file', file=mylogfile)
|
||||
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.
|
||||
``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.
|
||||
|
||||
|
||||
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.
|
||||
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?
|
||||
* 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.)
|
||||
print in stderr items,....
|
||||
print + stderr items,.......
|
||||
print[stderr] items,.....
|
||||
print to stderr items,.....
|
||||
|
||||
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'.
|
||||
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.)
|
||||
|
||||
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.
|
||||
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'.
|
||||
|
||||
Challenge: Why does there have to be a comma between the file and
|
||||
the rest?
|
||||
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.
|
||||
|
||||
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.
|
||||
* Challenge: Why does there have to be a comma between the file and
|
||||
the rest?
|
||||
|
||||
print >>i +1, 2
|
||||
print >>a [1], 2
|
||||
print >>f (1), 2
|
||||
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.
|
||||
|
||||
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.
|
||||
print >>i +1, 2
|
||||
print >>a [1], 2
|
||||
print >>f (1), 2
|
||||
|
||||
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.
|
||||
* Challenge: Why do you need a syntax extension? Why not
|
||||
writeln(file, item, ...)?
|
||||
|
||||
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?
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Full example: in ABC,
|
||||
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?
|
||||
|
||||
WRITE 1
|
||||
WRITE 2/
|
||||
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.
|
||||
|
||||
has the same effect as
|
||||
Full example: in ABC,
|
||||
|
||||
print 1,
|
||||
print 2
|
||||
::
|
||||
|
||||
has in Python, outputting in effect "1 2\n".
|
||||
WRITE 1
|
||||
WRITE 2/
|
||||
|
||||
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! :-)
|
||||
has the same effect as::
|
||||
|
||||
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.
|
||||
print 1,
|
||||
print 2
|
||||
|
||||
Challenge: Why not > instead of >>?
|
||||
has in Python, outputting in effect "1 2\n".
|
||||
|
||||
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.
|
||||
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! :-)
|
||||
|
||||
Challenge: But in C++, >> is input and << is output!
|
||||
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.
|
||||
|
||||
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: Why not > instead of >>?
|
||||
|
||||
Challenge: Surely you can design a println() function can do all
|
||||
what print>>file can do; why isn't that enough?
|
||||
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.
|
||||
|
||||
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:
|
||||
* Challenge: But in C++, >> is input and << is output!
|
||||
|
||||
def tables(n):
|
||||
for j in range(1, n+1):
|
||||
for i in range(1, n+1):
|
||||
print i, 'x', j, '=', i*j
|
||||
print
|
||||
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.
|
||||
|
||||
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:
|
||||
* Challenge: Surely you can design a ``println()`` function can do all
|
||||
what ``print>>file`` can do; why isn't that enough?
|
||||
|
||||
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
|
||||
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::
|
||||
|
||||
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):
|
||||
print i, 'x', j, '=', i*j
|
||||
print
|
||||
|
||||
def tables(n):
|
||||
for j in range(1, n+1):
|
||||
for i in range(1, n+1):
|
||||
println(i, 'x', j, '=', i*j)
|
||||
println()
|
||||
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::
|
||||
|
||||
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):
|
||||
print >> file, i, 'x', j, '=', i*j
|
||||
print >> file
|
||||
|
||||
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)
|
||||
With only a print statement and a ``println()`` function, the
|
||||
programmer first has to learn about ``println()``, transforming the
|
||||
original program to using ``println()``::
|
||||
|
||||
Thus, the transformation path is longer:
|
||||
def tables(n):
|
||||
for j in range(1, n+1):
|
||||
for i in range(1, n+1):
|
||||
println(i, 'x', j, '=', i*j)
|
||||
println()
|
||||
|
||||
(1) print
|
||||
(2) print >> file
|
||||
and **then** about the file keyword argument::
|
||||
|
||||
vs.
|
||||
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)
|
||||
|
||||
(1) print
|
||||
(2) println()
|
||||
(3) println(file=...)
|
||||
Thus, the transformation path is longer::
|
||||
|
||||
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:
|
||||
(1) print
|
||||
(2) print >> file
|
||||
|
||||
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
|
||||
vs.
|
||||
|
||||
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
|
||||
(1) print
|
||||
(2) println()
|
||||
(3) println(file=...)
|
||||
|
||||
(or, of course, print >> x where x is a variable whose value is
|
||||
None) means the same as
|
||||
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::
|
||||
|
||||
print foo, bar
|
||||
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
|
||||
|
||||
and the tables() function can be written as follows:
|
||||
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,
|
||||
|
||||
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
|
||||
::
|
||||
|
||||
[XXX this needs more justification, and a section of its own]
|
||||
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
|
||||
|
||||
.. XXX this needs more justification, and a section of its own
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
[1] http://docs.python.org/reference/simple_stmts.html#print
|
||||
[2] http://sourceforge.net/patch/download.php?id=100970
|
||||
.. [1] http://docs.python.org/reference/simple_stmts.html#print
|
||||
.. [2] http://sourceforge.net/patch/download.php?id=100970
|
||||
|
||||
|
||||
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
..
|
||||
Local Variables:
|
||||
mode: indented-text
|
||||
indent-tabs-mode: nil
|
||||
End:
|
||||
|
|
Loading…
Reference in New Issue