Search code examples
pythoncsvoopinputio

Explanation for one read operation per open() statement behavior?


Recently I came across a strange behavior of the with open() statement in Python. The following code returns output just for the first read-statement, having an empty lines-list.

input_csv = []
with open(self.path, 'r') as f:  # Opening the CSV
    r = csv.DictReader(f)
    for row in r:
        input_csv.append(row)  # Storing its contents in a dictionary for later use
    lines = f.readlines()  # Reading it in as a list too
    f.close()

While splitting it into two open () statements returns the objects as desired.

input_csv = []
with open(self.path, 'r') as f:  # Opening the CSV
    r = csv.DictReader(f)
    for row in r:
        input_csv.append(row)  # Storing its contents in a dictionary for later use
    f.close()

with open(self.path, 'r') as f:  # Opening the CSV
    lines = f.readlines()  # Reading it in as a list too
    f.close()

Why is the f variable just used once in the first statement?

Many thanks


Solution

  • If you look into documentation of csv.reader() which is used for DictReader().reader:

    Return a reader object which will iterate over lines in the given csvfile. csvfile can be any object which supports the iterator protocol and returns a string each time its __next__() method is called...

    Hence, it uses behavior of file-like object for which each iteration essentially is f.readline(). An operation which also advances current position in the file... until EOF is reached, which when iteration raises StopIteration exception. It is the same behavior you would observe trying:

    with open(self.path, 'r') as f:
        for l in f:
            pass  # each line was read
        print(f.readlines())
    

    You can add print(f.tell()) to see how the position changes as you execute each line.

    If you (re)open a new file, you start at position 0 (again). If you've read through once and wanted to use the same handle again, you need to return to the beginning of the file: f.seek(0).

    Note: you really do not need to perform f.close() in a managed context using with. Once you leave it, it'll close the file handle for you.