Permalink
Cannot retrieve contributors at this time
390 lines (293 sloc)
13 KB
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| PEP: 214 | |
| Title: Extended Print Statement | |
| Version: $Revision$ | |
| 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. | |
| 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 >> sys.stdout | |
| These two statements are syntax errors:: | |
| 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: | |
| - 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:: | |
| 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. | |
| 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. | |
| (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:: | |
| 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. | |
| 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 wouldn'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 | |
| 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 | |
| .. 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 | |
| .. | |
| Local Variables: | |
| mode: indented-text | |
| indent-tabs-mode: nil | |
| End: |