Книга: Practical Programming, Fourth Edition
Назад: Multiline Records
Дальше: Notes to File Away

Looking Ahead

Let’s add one final complication. Suppose that molecules didn’t have END markers but instead just a COMPND line followed by one or more ATOM lines. How would you read multiple molecules from a single file in that case?

 COMPND AMMONIA
 ATOM 1 N 0.257 -0.363 0.000
 ATOM 2 H 0.257 0.727 0.000
 ATOM 3 H 0.771 -0.727 0.890
 ATOM 4 H 0.771 -0.727 -0.890
 COMPND METHANOL
 ATOM 1 C -0.748 -0.015 0.024
 ATOM 2 O 0.558 0.420 -0.278
 ATOM 3 H -1.293 -0.202 -0.901
 ATOM 4 H -1.263 0.754 0.600
 ATOM 5 H -0.699 -0.934 0.609
 ATOM 6 H 0.716 1.404 0.137

At first glance, it doesn’t seem much different from the problem you just solved: read_molecule could extract the molecule’s name from the COMPND line and then read ATOM lines until it got either an empty string signaling the end of the file or another COMPND line signaling the start of the next molecule. However, once it has read the COMPND line, the line is no longer available for the next call to read_molecule, so how can you obtain the name of the second molecule (and all the ones following it)?

To solve this problem, your functions must always “look ahead” one line at a time. Let’s start with the function that reads multiple molecules:

 from​ ​typing​ ​import​ TextIO
 
 def​ ​read_all_molecules​(reader: TextIO) -> list:
 """Read zero or more molecules from reader and
  return a list of the molecules read.
  """
 
  result = []
  line = reader.readline()
 while​ line:
» molecule, line = read_molecule(reader, line)
  result.append(molecule)
 
 return​ result

This function begins by reading the first line of the file. Provided that line is not the empty string (that is, the file being read is not empty), it passes both the opened file to read from and the line into read_molecule, which is supposed to return two things: the next molecule in the file and the first line immediately after the end of that molecule (or an empty string if the end of the file has been reached).

This simple description is enough to get you started writing the read_molecule function. First it needs to verify that line indeed marks the start of a molecule. It then reads lines from reader one at a time, looking for one of three situations:

The most important thing is that when this function returns, it returns both the molecule and the next line so that its caller can keep processing. The result is probably the most complicated function you have seen so far, but understanding the idea behind it will help you know how it works:

 from​ ​typing​ ​import​ TextIO
 
 def​ ​read_molecule​(reader: TextIO, line: str) -> list:
 """Read a molecule from reader, where line refers to the first line of
  the molecule to be read. Return the molecule and the first line after
  it (or the empty string if the end of file has been reached).
  """
 
  fields = line.split()
  molecule = [fields[1]]
 
  line = reader.readline()
 while​ line ​and​ ​not​ line.startswith(​'COMPND'​):
  fields = line.split()
 if​ fields[0] == ​'ATOM'​:
» key, num, atom_type, x, y, z = fields
  molecule.append([atom_type, x, y, z])
  line = reader.readline()
 
 return​ molecule, line

Finally, the line highlighted in the code fragment demonstrates an unpacking operation, where the list elements are simultaneously assigned to the variables key, num, atom_type, x, y, and z in a single statement. For such an assignment to work, the number of variables on the LHS must equal the number of elements in the list on the RHS. See , for more information on simultaneous assignments.

Incidentally, the highlighted lines in the code fragments and utilize tuples (“immutable lists”). You’ll learn more about them in .

Назад: Multiline Records
Дальше: Notes to File Away