You may already be familiar with the notion of files, which are named storage compartments on your computer that are managed by your operating system. The last major built-in object type that we’ll examine on our object types tour provides a way to access those files inside Python programs.
In short, the built-in open function creates a Python file object, which serves as a link to a file residing on your machine. After calling open, you can transfer strings of data to and from the associated external file by calling the returned file object’s methods.
Compared to the types you’ve seen so far, file objects are somewhat unusual. They are considered a core type because they are created by a built-in function, but they’re not numbers, sequences, or mappings, and they don’t respond to expression operators; they export only methods for common file-processing tasks. Most file methods are concerned with performing input from and output to the external file associated with a file object, but other file methods allow us to seek to a new position in the file, flush output buffers, and so on. Table 9-2 summarizes common file operations.
Table 9-2. Common file operations
|
Operation |
Interpretation |
|---|---|
|
|
|
|
|
|
|
|
Same as prior line ( |
|
|
Read entire file into a single string |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
File iterators read line by line |
|
|
Python 3.X Unicode text files ( |
|
|
Python 3.X bytes files ( |
|
|
|
|
|
Python 2.X bytes files ( |
To open a file, a program calls the built-in open function, with the external filename first, followed by a processing mode. The call returns a file object, which in turn has methods for data transfer:
afile = open(filename,mode) afile.method()
The first argument to open, the external filename, may include a platform-specific and absolute or relative directory path prefix. Without a directory path, the file is assumed to exist in the current working directory (i.e., where the script runs). As we’ll see in Chapter 37’s expanded file coverage, the filename may also contain non-ASCII Unicode characters that Python automatically translates to and from the underlying platform’s encoding, or be provided as a pre-encoded byte string.
The second argument to open, processing mode, is typically the string 'r' to open for text input (the default), 'w' to create and open for text output, or 'a' to open for appending text to the end (e.g., for adding to logfiles). The processing mode argument can specify additional options:
Adding a b to the mode string allows for binary data (end-of-line translations and 3.X Unicode encodings are turned off).
Adding a + opens the file for both input and output (i.e., you can both read and write to the same file object, often in conjunction with seek operations to reposition in the file).
Both of the first two arguments to open must be Python strings. An optional third argument can be used to control output buffering—passing a zero means that output is unbuffered (it is transferred to the external file immediately on a write method call), and additional arguments may be provided for special types of files (e.g., an encoding for Unicode text files in Python 3.X).
We’ll cover file fundamentals and explore some basic examples here, but we won’t go into all file-processing mode options; as usual, consult the Python library manual for additional details.
Once you make a file object with open, you can call its methods to read from or write to the associated external file. In all cases, file text takes the form of strings in Python programs; reading a file returns its content in strings, and content is passed to the write methods as strings. Reading and writing methods come in multiple flavors; Table 9-2 lists the most common. Here are a few fundamental usage notes:
Though the reading and writing methods in the table are common, keep in mind that probably the best way to read lines from a text file today is to not read the file at all—as we’ll see in Chapter 14, files also have an iterator that automatically reads one line at a time in a for loop, list comprehension, or other iteration context.
Notice in Table 9-2 that data read from a file always comes back to your script as a string, so you’ll have to convert it to a different type of Python object if a string is not what you need. Similarly, unlike with the print operation, Python does not add any formatting and does not convert objects to strings automatically when you write data to a file—you must send an already formatted string. Because of this, the tools we have already met to convert objects to and from strings (e.g., int, float, str, and the string formatting expression and method) come in handy when dealing with files.
Python also includes advanced standard library tools for handling generic object storage (the pickle module), for dealing with packed binary data in files (the struct module), and for processing special types of content such as JSON, XML, and CSV text. We’ll see these at work later in this chapter and book, but Python’s manuals document them in full.
By default, output files are always buffered, which means that text you write may not be transferred from memory to disk immediately—closing a file, or running its flush method, forces the buffered data to disk. You can avoid buffering with extra open arguments, but it may impede performance. Python files are also random-access on a byte offset basis—their seek method allows your scripts to jump around to read and write at specific locations.
close is often optional: auto-close on collectionCalling the file close method terminates your connection to the external file, releases its system resources, and flushes its buffered output to disk if any is still in memory. As discussed in Chapter 6, in Python an object’s memory space is automatically reclaimed as soon as the object is no longer referenced anywhere in the program. When file objects are reclaimed, Python also automatically closes the files if they are still open (this also happens when a program shuts down). This means you don’t always need to manually close your files in standard Python, especially those in simple scripts with short runtimes, and temporary files used by a single line or expression.
On the other hand, including manual close calls doesn’t hurt, and may be a good habit to form, especially in long-running systems. Strictly speaking, this auto-close-on-collection feature of files is not part of the language definition—it may change over time, may not happen when you expect it to in interactive shells, and may not work the same in other Python implementations whose garbage collectors may not reclaim and close files at the same points as standard CPython. In fact, when many files are opened within loops, Pythons other than CPython may require close calls to free up system resources immediately, before garbage collection can get around to freeing objects. Moreover, close calls may sometimes be required to flush buffered output of file objects not yet reclaimed. For an alternative way to guarantee automatic file closes, also see this section’s later discussion of the file object’s context manager, used with the with/as statement in Python 2.6, 2.7, and 3.X.
Let’s work through a simple example that demonstrates file-processing basics. The following code begins by opening a new text file for output, writing two lines (strings terminated with a newline marker, \n), and closing the file. Later, the example opens the same file again in input mode and reads the lines back one at a time with readline. Notice that the third readline call returns an empty string; this is how Python file methods tell you that you’ve reached the end of the file (empty lines in the file come back as strings containing just a newline character, not as empty strings). Here’s the complete interaction:
>>>myfile = open('myfile.txt', 'w')# Open for text output: create/empty >>>myfile.write('hello text file\n')# Write a line of text: string 16 >>>myfile.write('goodbye text file\n')18 >>>myfile.close()# Flush output buffers to disk >>>myfile = open('myfile.txt')# Open for text input: 'r' is default >>>myfile.readline()# Read the lines back 'hello text file\n' >>>myfile.readline()'goodbye text file\n' >>>myfile.readline()# Empty string: end-of-file ''
Notice that file write calls return the number of characters written in Python 3.X; in 2.X they don’t, so you won’t see these numbers echoed interactively. This example writes each line of text, including its end-of-line terminator, \n, as a string; write methods don’t add the end-of-line character for us, so we must include it to properly terminate our lines (otherwise the next write will simply extend the current line in the file).
If you want to display the file’s content with end-of-line characters interpreted, read the entire file into a string all at once with the file object’s read method and print it:
>>>open('myfile.txt').read()# Read all at once into string 'hello text file\ngoodbye text file\n' >>>print(open('myfile.txt').read())# User-friendly display hello text file goodbye text file
And if you want to scan a text file line by line, file iterators are often your best option:
>>>for line in open('myfile.txt'):# Use file iterators, not reads ...print(line, end='')... hello text file goodbye text file
When coded this way, the temporary file object created by open will automatically read and return one line on each loop iteration. This form is usually easiest to code, good on memory use, and may be faster than some other options (depending on many variables, of course). Since we haven’t reached statements or iterators yet, though, you’ll have to wait until Chapter 14 for a more complete explanation of this code.
Windows users: As mentioned in Chapter 7, open accepts Unix-style forward slashes in place of backward slashes on Windows, so any of the following forms work for directory paths—raw strings, forward slashes, or doubled-up backslashes:
>>>open(r'C:\Python33\Lib\pdb.py').readline()'#! /usr/bin/env python3\n' >>>open('C:/Python33/Lib/pdb.py').readline()'#! /usr/bin/env python3\n' >>>open('C:\\Python33\\Lib\\pdb.py').readline()'#! /usr/bin/env python3\n'
The raw string form in the second command is still useful to turn off accidental escapes when you can’t control string content, and in other contexts.
Strictly speaking, the example in the prior section uses text files. In both Python 3.X and 2.X, file type is determined by the second argument to open, the mode string—an included “b” means binary. Python has always supported both text and binary files, but in Python 3.X there is a sharper distinction between the two:
In contrast, Python 2.X text files handle both 8-bit text and binary data, and a special string type and file interface (unicode strings and codecs.open) handles Unicode text. The differences in Python 3.X stem from the fact that simple and Unicode text have been merged in the normal string type—which makes sense, given that all text is Unicode, including ASCII and other 8-bit encodings.
Because most programmers deal only with ASCII text, they can get by with the basic text file interface used in the prior example, and normal strings. All strings are technically Unicode in 3.X, but ASCII users will not generally notice. In fact, text files and strings work the same in 3.X and 2.X if your script’s scope is limited to such simple forms of text.
If you need to handle internationalized applications or byte-oriented data, though, the distinction in 3.X impacts your code (usually for the better). In general, you must use bytes strings for binary files, and normal str strings for text files. Moreover, because text files implement Unicode encodings, you should not open a binary data file in text mode—decoding its content to Unicode text will likely fail.
Let’s look at an example. When you read a binary data file you get back a bytes object—a sequence of small integers that represent absolute byte values (which may or may not correspond to characters), which looks and feels almost exactly like a normal string. In Python 3.X, and assuming an existing binary file:
>>>data = open('data.bin', 'rb').read()# Open binary file: rb=read binary >>>data# bytes string holds binary data b'\x00\x00\x00\x07spam\x00\x08' >>>data[4:8]# Act like strings b'spam' >>>data[4:8][0]# But really are small 8-bit integers 115 >>>bin(data[4:8][0])# Python 3.X/2.6+ bin() function '0b1110011'
In addition, binary files do not perform any end-of-line translation on data; text files by default map all forms to and from \n when written and read and implement Unicode encodings on transfers in 3.X. Binary files like this one work the same in Python 2.X, but byte strings are simply normal strings and have no leading b when displayed, and text files must use the codecs module to add Unicode processing.
Per the note at the start of this chapter, though, that’s as much as we’re going to say about Unicode text and binary data files here, and just enough to understand upcoming examples in this chapter. Since the distinction is of marginal interest to many Python programmers, we’ll defer to the files preview in Chapter 4 for a quick tour and postpone the full story until Chapter 37. For now, let’s move on to some more substantial file examples to demonstrate a few common use cases.
Our next example writes a variety of Python objects into a text file on multiple lines. Notice that it must convert objects to strings using conversion tools. Again, file data is always strings in our scripts, and write methods do not do any automatic to-string formatting for us (for space, I’m omitting byte-count return values from write methods from here on):
>>>X, Y, Z = 43, 44, 45# Native Python objects >>>S = 'Spam'# Must be strings to store in file >>>D = {'a': 1, 'b': 2}>>>L = [1, 2, 3]>>> >>>F = open('datafile.txt', 'w')# Create output text file >>>F.write(S + '\n')# Terminate lines with \n >>>F.write('%s,%s,%s\n' % (X, Y, Z))# Convert numbers to strings >>>F.write(str(L) + '$' + str(D) + '\n')# Convert and separate with $ >>>F.close()
Once we have created our file, we can inspect its contents by opening it and reading it into a string (strung together as a single operation here). Notice that the interactive echo gives the exact byte contents, while the print operation interprets embedded end-of-line characters to render a more user-friendly display:
>>>chars = open('datafile.txt').read()# Raw string display >>>chars"Spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n" >>>print(chars)# User-friendly display Spam 43,44,45 [1, 2, 3]${'a': 1, 'b': 2}
We now have to use other conversion tools to translate from the strings in the text file to real Python objects. As Python never converts strings to numbers (or other types of objects) automatically, this is required if we need to gain access to normal object tools like indexing, addition, and so on:
>>>F = open('datafile.txt')# Open again >>>line = F.readline()# Read one line >>>line'Spam\n' >>>line.rstrip()# Remove end-of-line 'Spam'
For this first line, we used the string rstrip method to get rid of the trailing end-of-line character; a line[:−1] slice would work, too, but only if we can be sure all lines end in the \n character (the last line in a file sometimes does not).
So far, we’ve read the line containing the string. Now let’s grab the next line, which contains numbers, and parse out (that is, extract) the objects on that line:
>>>line = F.readline()# Next line from file >>>line# It's a string here '43,44,45\n' >>>parts = line.split(',')# Split (parse) on commas >>>parts['43', '44', '45\n']
We used the string split method here to chop up the line on its comma delimiters; the result is a list of substrings containing the individual numbers. We still must convert from strings to integers, though, if we wish to perform math on these:
>>>int(parts[1])# Convert from string to int 44 >>>numbers = [int(P) for P in parts]# Convert all in list at once >>>numbers[43, 44, 45]
As we have learned, int translates a string of digits into an integer object, and the list comprehension expression introduced in Chapter 4 can apply the call to each item in our list all at once (you’ll find more on list comprehensions later in this book). Notice that we didn’t have to run rstrip to delete the \n at the end of the last part; int and some other converters quietly ignore whitespace around digits.
Finally, to convert the stored list and dictionary in the third line of the file, we can run them through eval, a built-in function that treats a string as a piece of executable program code (technically, a string containing a Python expression):
>>>line = F.readline()>>>line"[1, 2, 3]${'a': 1, 'b': 2}\n" >>>parts = line.split('$')# Split (parse) on $ >>>parts['[1, 2, 3]', "{'a': 1, 'b': 2}\n"] >>>eval(parts[0])# Convert to any object type [1, 2, 3] >>>objects = [eval(P) for P in parts]# Do same for all in list >>>objects[[1, 2, 3], {'a': 1, 'b': 2}]
Because the end result of all this parsing and converting is a list of normal Python objects instead of strings, we can now apply list and dictionary operations to them in our script.
Using eval to convert from strings to objects, as demonstrated in the preceding code, is a powerful tool. In fact, sometimes it’s too powerful. eval will happily run any Python expression—even one that might delete all the files on your computer, given the necessary permissions! If you really want to store native Python objects, but you can’t trust the source of the data in the file, Python’s standard library pickle module is ideal.
The pickle module is a more advanced tool that allows us to store almost any Python object in a file directly, with no to- or from-string conversion requirement on our part. It’s like a super-general data formatting and parsing utility. To store a dictionary in a file, for instance, we pickle it directly:
>>>D = {'a': 1, 'b': 2}>>>F = open('datafile.pkl', 'wb')>>>import pickle>>>pickle.dump(D, F)# Pickle any object to file >>>F.close()
Then, to get the dictionary back later, we simply use pickle again to re-create it:
>>>F = open('datafile.pkl', 'rb')>>>E = pickle.load(F)# Load any object from file >>>E{'a': 1, 'b': 2}
We get back an equivalent dictionary object, with no manual splitting or converting required. The pickle module performs what is known as object serialization—converting objects to and from strings of bytes—but requires very little work on our part. In fact, pickle internally translates our dictionary to a string form, though it’s not much to look at (and may vary if we pickle in other data protocol modes):
>>> open('datafile.pkl', 'rb').read() # Format is prone to change!
b'\x80\x03}q\x00(X\x01\x00\x00\x00bq\x01K\x02X\x01\x00\x00\x00aq\x02K\x01u.'
Because pickle can reconstruct the object from this format, we don’t have to deal with it ourselves. For more on the pickle module, see the Python standard library manual, or import pickle and pass it to help interactively. While you’re exploring, also take a look at the shelve module. shelve is a tool that uses pickle to store Python objects in an access-by-key filesystem, which is beyond our scope here (though you will get to see an example of shelve in action in Chapter 28, and other pickle examples in Chapter 31 and Chapter 37).
Notice that I opened the file used to store the pickled object in binary mode; binary mode is always required in Python 3.X, because the pickler creates and uses a bytes string object, and these objects imply binary-mode files (text-mode files imply str strings in 3.X). In earlier Pythons it’s OK to use text-mode files for protocol 0 (the default, which creates ASCII text), as long as text mode is used consistently; higher protocols require binary-mode files. Python 3.X’s default protocol is 3 (binary), but it creates bytes even for protocol 0. See Chapter 28, Chapter 31, and Chapter 37; Python’s library manual; or reference books for more details on and examples of pickled data.
Python 2.X also has a cPickle module, which is an optimized version of pickle that can be imported directly for speed. Python 3.X renames this module _pickle and uses it automatically in pickle—scripts simply import pickle and let Python optimize itself.
The prior section’s pickle module translates nearly arbitrary Python objects to a proprietary format developed specifically for Python, and honed for performance over many years. JSON is a newer and emerging data interchange format, which is both programming-language-neutral and supported by a variety of systems. MongoDB, for instance, stores data in a JSON document database (using a binary JSON format).
JSON does not support as broad a range of Python object types as pickle, but its portability is an advantage in some contexts, and it represents another way to serialize a specific category of Python objects for storage and transmission. Moreover, because JSON is so close to Python dictionaries and lists in syntax, the translation to and from Python objects is trivial, and is automated by the json standard library module.
For example, a Python dictionary with nested structures is very similar to JSON data, though Python’s variables and expressions support richer structuring options (any part of the following can be an arbitrary expression in Python code):
>>>name = dict(first='Bob', last='Smith')>>>rec = dict(name=name, job=['dev', 'mgr'], age=40.5)>>>rec{'job': ['dev', 'mgr'], 'name': {'last': 'Smith', 'first': 'Bob'}, 'age': 40.5}
The final dictionary format displayed here is a valid literal in Python code, and almost passes for JSON when printed as is, but the json module makes the translation official—here translating Python objects to and from a JSON serialized string representation in memory:
>>>import json>>>json.dumps(rec)'{"job": ["dev", "mgr"], "name": {"last": "Smith", "first": "Bob"}, "age": 40.5}' >>>S = json.dumps(rec)>>>S'{"job": ["dev", "mgr"], "name": {"last": "Smith", "first": "Bob"}, "age": 40.5}' >>>O = json.loads(S)>>>O{'job': ['dev', 'mgr'], 'name': {'last': 'Smith', 'first': 'Bob'}, 'age': 40.5} >>>O == recTrue
It’s similarly straightforward to translate Python objects to and from JSON data strings in files. Prior to being stored in a file, your data is simply Python objects; the JSON module recreates them from the JSON textual representation when it loads it from the file:
>>>json.dump(rec, fp=open('testjson.txt', 'w'), indent=4)>>>print(open('testjson.txt').read()){ "job": [ "dev", "mgr" ], "name": { "last": "Smith", "first": "Bob" }, "age": 40.5 } >>>P = json.load(open('testjson.txt'))>>>P{'job': ['dev', 'mgr'], 'name': {'last': 'Smith', 'first': 'Bob'}, 'age': 40.5}
Once you’ve translated from JSON text, you process the data using normal Python object operations in your script. For more details on JSON-related topics, see Python’s library manuals and search the Web.
Note that strings are all Unicode in JSON to support text drawn from international character sets, so you’ll see a leading u on strings after translating from JSON data in Python 2.X (but not in 3.X); this is just the syntax of Unicode objects in 2.X, as introduced Chapter 4 and Chapter 7, and covered in full in Chapter 37. Because Unicode text strings support all the usual string operations, the difference is negligible to your code while text resides in memory; the distinction matters most when transferring text to and from files, and then usually only for non-ASCII types of text where encodings come into play.
There is also support in the Python world for translating objects to and from XML, a text format used in Chapter 37; see the web for details.For another semirelated tool that deals with formatted data files, see the standard library’s csv module. It parses and creates CSV (comma-separated value) data in files and strings. This doesn’t map as directly to Python objects, but is another common data exchange format:
>>>import csv>>>rdr = csv.reader(open('csvdata.txt'))>>>for row in rdr: print(row)... ['a', 'bbb', 'cc', 'dddd'] ['11', '22', '33', '44']
One other file-related note before we move on: some advanced applications also need to deal with packed binary data, created perhaps by a C language program or a network connection. Python’s standard library includes a tool to help in this domain—the struct module knows how to both compose and parse packed binary data. In a sense, this is another data-conversion tool that interprets strings in files as binary data.
We saw an overview of this tool in Chapter 4, but let’s take another quick look here for more perspective. To create a packed binary data file, open it in 'wb' (write binary) mode, and pass struct a format string and some Python objects. The format string used here means pack as a 4-byte integer, a 4-character string (which must be a bytes string as of Python 3.2), and a 2-byte integer, all in big-endian form (other format codes handle padding bytes, floating-point numbers, and more):
>>>F = open('data.bin', 'wb')# Open binary output file >>>import struct>>>data = struct.pack('>i4sh', 7, b'spam', 8)# Make packed binary data >>>datab'\x00\x00\x00\x07spam\x00\x08' >>>F.write(data)# Write byte string >>>F.close()
Python creates a binary bytes data string, which we write out to the file normally—this one consists mostly of nonprintable characters printed in hexadecimal escapes, and is the same binary file we met earlier. To parse the values out to normal Python objects, we simply read the string back and unpack it using the same format string. Python extracts the values into normal Python objects—integers and a string:
>>>F = open('data.bin', 'rb')>>>data = F.read()# Get packed binary data >>>datab'\x00\x00\x00\x07spam\x00\x08' >>>values = struct.unpack('>i4sh', data)# Convert to Python objects >>>values(7, b'spam', 8)
Binary data files are advanced and somewhat low-level tools that we won’t cover in more detail here; for more help, see the struct coverage in Chapter 37, consult the Python library manual, or import struct and pass it to the help function interactively. Also note that you can use the binary file-processing modes 'wb' and 'rb' to process a simpler binary file, such as an image or audio file, as a whole without having to unpack its contents; in such cases your code might pass it unparsed to other files or tools.
You’ll also want to watch for Chapter 34’s discussion of the file’s context manager support, new as of Python 3.0 and 2.6. Though more a feature of exception processing than files themselves, it allows us to wrap file-processing code in a logic layer that ensures that the file will be closed (and if needed, have its output flushed to disk) automatically on exit, instead of relying on the auto-close during garbage collection:
with open(r'C:\code\data.txt') as myfile: # See Chapter 34 for details
for line in myfile:
...use line here...
The try/finally statement that we’ll also study in Chapter 34 can provide similar functionality, but at some cost in extra code—three extra lines, to be precise (though we can often avoid both options and let Python close files for us automatically):
myfile = open(r'C:\code\data.txt')
try:
for line in myfile:
...use line here...
finally:
myfile.close()
The with context manager scheme ensures release of system resources in all Pythons, and may be more useful for output files to guarantee buffer flushes; unlike the more general try, though, it is also limited to objects that support its protocol. Since both these options require more information than we have yet obtained, however, we’ll postpone details until later in this book.
There are additional, more specialized file methods shown in Table 9-2, and even more that are not in the table. For instance, as mentioned earlier, seek resets your current position in a file (the next read or write happens at that position), flush forces buffered output to be written out to disk without closing the connection (by default, files are always buffered), and so on.
The Python standard library manual and the reference books described in the preface provide complete lists of file methods; for a quick look, run a dir or help call interactively, passing in an open file object (in Python 2.X but not 3.X, you can pass in the name file instead). For more file-processing examples, watch for the sidebar Why You Will Care: File Scanners in Chapter 13. It sketches common file-scanning loop code patterns with statements we have not covered enough yet to use here.
Also, note that although the open function and the file objects it returns are your main interface to external files in a Python script, there are additional file-like tools in the Python toolset. Among these:
Preopened file objects in the sys module, such as sys.stdout (see Print Operations in Chapter 11 for details)
os moduleInteger file handles that support lower-level tools such as file locking (see also the “x” mode in Python 3.3’s open for exclusive creation)
File-like objects used to synchronize processes or communicate over networks
Used to store unaltered and pickled Python objects directly, by key (used in Chapter 28)
Tools such as os.popen and subprocess.Popen that support spawning shell commands and reading and writing to their standard streams (see Chapter 13 and Chapter 21 for examples)
The third-party open source domain offers even more file-like tools, including support for communicating with serial ports in the PySerial extension and interactive programs in the pexpect system. See applications-focused Python texts and the Web at large for additional information on file-like tools.
Version skew note: In Python 2.X, the built-in name open is essentially a synonym for the name file, and you may technically open files by calling either open or file (though open is generally preferred for opening). In Python 3.X, the name file is no longer available, because of its redundancy with open.
Python 2.X users may also use the name file as the file object type, in order to customize files with object-oriented programming (described later in this book). In Python 3.X, files have changed radically. The classes used to implement file objects live in the standard library module io. See this module’s documentation or code for the classes it makes available for customization, and run a type(F) call on an open file F for hints.