For the next set of examples we're going to need a data file containing some numbers. Using the code in recaman.py below, we'll write a sequence of numbers called Recaman's sequence to a text file, with one number per line:
import sys
from itertools import count, islice
def sequence():
"""Generate Recaman's sequence."""
seen = set()
a = 0
for n in count(1):
yield a
seen.add(a)
c = a - n
if c < 0 or c in seen:
c = a + n
a = c
def write_sequence(filename, num):
"""Write Recaman's sequence to a text file."""
f = open(filename, mode='wt', encoding='utf-8')
f.writelines("{0} ".format(r)
for r in islice(sequence(), num + 1))
f.close()
if __name__ == '__main__':
write_sequence(filename=sys.argv[1],
num=int(sys.argv[2]))
Recaman's sequence itself isn't important to this exercise; we just needed a way of generating numeric data. As such, we won't be explaining the sequence() generator. Feel free to experiment though.
The module contains a generator for yielding the Recaman numbers and a function which writes the start of the sequence to file using the writelines() method. A generator expression is used to convert each number to a string and add a newline. itertools.islice() is used to truncate the otherwise infinite sequence.
We'll write the first 1000 Recaman numbers to a file by executing the module, passing the filename and series length as command line arguments:
$ python3 recaman.py recaman.dat 1000
Now let's make a complementary module series.py which reads this data file back in:
"""Read and print an integer series."""
import sys
def read_series(filename):
f = open(filename, mode='rt', encoding='utf-8')
series = []
for line in f:
a = int(line.strip())
series.append(a)
f.close()
return series
def main(filename):
series = read_series(filename)
print(series)
if __name__ == '__main__':
main(sys.argv[1])
We simply read one line at a time from the open file, strip the newline with a call to the strip() string method, and convert it to an integer. If we run it from the command line, everything should work as expected:
$ python3 series.py recaman.dat
[0, 1, 3, 6, 2, 7, 13,
...
,3683, 2688, 3684, 2687, 3685, 2686, 3686]
Now let's deliberately create an exceptional situation. Open recaman.dat in a text editor and replace one of the numbers with something that isn't an stringified integer:
0
1
3
6
2
7
13
oops!
12
21
Save the file, and re-run series.py:
$ python3 series.py recaman.dat
Traceback (most recent call last):
File "series.py", line 19, in <module>
main(sys.argv[1])
File "series.py", line 15, in main
series = read_series(filename)
File "series.py", line 9, in read_series
a = int(line.strip())
ValueError: invalid literal for int() with base 10: 'oops!'
The int() constructor raises a ValueError when passed our new, invalid line. The exception is unhandled, and so the program terminates with stack trace.