预计阅读本页时间:-
Print Stream Redirection
In both Python 3.0 and 2.6, printing sends text to the standard output stream by default. However, it’s often useful to send it elsewhere—to a text file, for example, to save results for later use or testing purposes. Although such redirection can be accomplished in system shells outside Python itself, it turns out to be just as easy to redirect a script’s streams from within the script.
The Python “hello world” program
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
Let’s start off with the usual (and largely pointless) language benchmark—the “hello world” program. To print a “hello world” message in Python, simply print the string per your version’s print operation:
>>> print('hello world') # Print a string object in 3.0
hello world
>>> print 'hello world' # Print a string object in 2.6
hello world
Because expression results are echoed on the interactive command line, you often don’t even need to use a print statement there—simply type the expressions you’d like to have printed, and their results are echoed back:
>>> 'hello world' # Interactive echoes
'hello world'
This code isn’t exactly an earth-shattering piece of software mastery, but it serves to illustrate printing behavior. Really, the print operation is just an ergonomic feature of Python—it provides a simple interface to the sys.stdout object, with a bit of default formatting. In fact, if you enjoy working harder than you must, you can also code print operations this way:
>>> import sys # Printing the hard way
>>> sys.stdout.write('hello world\n')
hello world
This code explicitly calls the write method of sys.stdout—an attribute preset when Python starts up to an open file object connected to the output stream. The print operation hides most of those details, providing a simple tool for simple printing tasks.
Manual stream redirection
So, why did I just show you the hard way to print? The sys.stdout print equivalent turns out to be the basis of a common technique in Python. In general, print and sys.stdout are directly related as follows. This statement:
print(X, Y) # Or, in 2.6: print X, Y
is equivalent to the longer:
import sys
sys.stdout.write(str(X) + ' ' + str(Y) + '\n')
which manually performs a string conversion with str, adds a separator and newline with +, and calls the output stream’s write method. Which would you rather code? (He says, hoping to underscore the programmer-friendly nature of prints....)
Obviously, the long form isn’t all that useful for printing by itself. However, it is useful to know that this is exactly what print operations do because it is possible to reassign sys.stdout to something different from the standard output stream. In other words, this equivalence provides a way of making your print operations send their text to other places. For example:
import sys
sys.stdout = open('log.txt', 'a') # Redirects prints to a file
...
print(x, y, x) # Shows up in log.txt
Here, we reset sys.stdout to a manually opened file named log.txt, located in the script’s working directory and opened in append mode (so we add to its current content). After the reset, every print operation anywhere in the program will write its text to the end of the file log.txt instead of to the original output stream. The print operations are happy to keep calling sys.stdout’s write method, no matter what sys.stdout happens to refer to. Because there is just one sys module in your process, assigning sys.stdout this way will redirect every print anywhere in your program.
In fact, as this chapter’s upcoming sidebar about print and stdout will explain, you can even reset sys.stdout to an object that isn’t a file at all, as long as it has the expected interface: a method named write to receive the printed text string argument. When that object is a class, printed text can be routed and processed arbitrarily per a write method you code yourself.
This trick of resetting the output stream is primarily useful for programs originally coded with print statements. If you know that output should go to a file to begin with, you can always call file write methods instead. To redirect the output of a print-based program, though, resetting sys.stdout provides a convenient alternative to changing every print statement or using system shell-based redirection syntax.
Automatic stream redirection
This technique of redirecting printed text by assigning sys.stdout is commonly used in practice. One potential problem with the last section’s code, though, is that there is no direct way to restore the original output stream should you need to switch back after printing to a file. Because sys.stdout is just a normal file object, you can always save it and restore it if needed:[30]
C:\misc> c:\python30\python
>>> import sys
>>> temp = sys.stdout # Save for restoring later
>>> sys.stdout = open('log.txt', 'a') # Redirect prints to a file
>>> print('spam') # Prints go to file, not here
>>> print(1, 2, 3)
>>> sys.stdout.close() # Flush output to disk
>>> sys.stdout = temp # Restore original stream
>>> print('back here') # Prints show up here again
back here
>>> print(open('log.txt').read()) # Result of earlier prints
spam
1 2 3
As you can see, though, manual saving and restoring of the original output stream like this involves quite a bit of extra work. Because this crops up fairly often, a print extension is available to make it unnecessary.
In 3.0, the file keyword allows a single print call to send its text to a file’s write method, without actually resetting sys.stdout. Because the redirection is temporary, normal print calls keep printing to the original output stream. In 2.6, a print statement that begins with a >> followed by an output file object (or other compatible object) has the same effect. For example, the following again sends printed text to a file named log.txt:
log = open('log.txt', 'a') # 3.0
print(x, y, z, file=log) # Print to a file-like object
print(a, b, c) # Print to original stdout
log = open('log.txt', 'a') # 2.6
print >> log, x, y, z # Print to a file-like object
print a, b, c # Print to original stdout
These redirected forms of print are handy if you need to print to both files and the standard output stream in the same program. If you use these forms, however, be sure to give them a file object (or an object that has the same write method as a file object), not a file’s name string. Here is the technique in action:
C:\misc> c:\python30\python
>>> log = open('log.txt', 'w')
>>> print(1, 2, 3, file=log) # 2.6: print >> log, 1, 2, 3
>>> print(4, 5, 6, file=log)
>>> log.close()
>>> print(7, 8, 9) # 2.6: print 7, 8, 9
7 8 9
>>> print(open('log.txt').read())
1 2 3
4 5 6
These extended forms of print are also commonly used to print error messages to the standard error stream, available to your script as the preopened file object sys.stderr. You can either use its file write methods and format the output manually, or print with redirection syntax:
>>> import sys
>>> sys.stderr.write(('Bad!' * 8) + '\n')
Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad!
>>> print('Bad!' * 8, file=sys.stderr) # 2.6: print >> sys.stderr, 'Bad' * 8
Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad!
Now that you know all about print redirections, the equivalence between printing and file write methods should be fairly obvious. The following interaction prints both ways in 3.0, then redirects the output to an external file to verify that the same text is printed:
>>> X = 1; Y = 2
>>> print(X, Y) # Print: the easy way
1 2
>>> import sys # Print: the hard way
>>> sys.stdout.write(str(X) + ' ' + str(Y) + '\n')
1 2
4
>>> print(X, Y, file=open('temp1', 'w')) # Redirect text to file
>>> open('temp2', 'w').write(str(X) + ' ' + str(Y) + '\n') # Send to file manually
4
>>> print(open('temp1', 'rb').read()) # Binary mode for bytes
b'1 2\r\n'
>>> print(open('temp2', 'rb').read())
b'1 2\r\n'
As you can see, unless you happen to enjoy typing, print operations are usually the best option for displaying text. For another example of the equivalence between prints and file writes, watch for a 3.0 print function emulation example in Chapter 18; it uses this code pattern to provide a general 3.0 print function equivalent for use in Python 2.6.