user179348
user179348

Reputation: 1

Python 3 combining file open and read commands - a need to close a file and how?

I am working through "Learn Python 3 the Hard Way" and am making code more concise. Lines 11 to 18 of the program below (line 1 starts at # program: p17.py) are relevant to my question. Opening and reading a file are very easy and it is easy to see how you close the file you open when working with the files. The original section is commented out and I include the concise code on line 16. I commented out the line of code that causes an error (on line 20):

$ python3 p17_aside.py p17_text.txt p17_to_file_3.py Copying from p17_text.txt to p17_to_file_3.py This is text.

Traceback (most recent call last): File "p17_aside.py", line 20, in indata.close() AttributeError: 'str' object has no attribute 'close'


Code is below:


# program: p17.py
# This program copies one file to another. It uses the argv function as well
# as exists - from sys and os.path modules respectively
from sys import argv
from os.path import exists

script, from_file, to_file = argv

print(f"Copying from {from_file} to {to_file}")

# we could do these two on one line, how?
#in_file = open(from_file)
#indata = in_file.read()
#print(indata)
# THE ANSWER -
indata = open(from_file).read()
# The next line was used for testing
print(indata)

# indata.close()

So my question is should I just avoid the practice of combining commands as done above or is there a way to properly deal with that situation so files are closed when they should be? Is it necessary to deal with the situation of closing a file at all in this situation?

Upvotes: 0

Views: 699

Answers (2)

user179348
user179348

Reputation: 1

How about the following: # to open the file and read it indata = open(from_file).read() print(indata) # this closes the file - just the opposite of opening and reading open(from_file).close()

Upvotes: -1

Ondrej K.
Ondrej K.

Reputation: 9664

Context manager and with statement is a comfortable way to make sure your file is closed as needed:

with open(from_file) as fobj:
    indata = fobj.read()

Nowadays, you can also use Path-like objects and their read_text and read_bytes methods:

# This assumes Path from pathlib has been imported
indata = Path(from_file).read_text()

The error you were seeing... is because you were not trying to close the file, but str into which you've read its content into. You'd need to assign object returned by open a name, and then read from and close that one:

fobj = open(from_file)
indata = fobj.read()
fobj.close()  # This is OK

Strictly speaking, you would not need to close that file as dangling file descriptors would be "clobbered" with the process. Esp. in a short example like this, it would be of relatively little concern.


I hope I got the follow up question in comment correctly to extend on this a bit more.

If you wanted a single command, look at the pathtlib.Path example above.

With open as such, you cannot perform read and close in a single operation and without assigning result of open to a variable. As both read and close would have to be performed on the same object returned by open. If you do:

var = fobj.read()

Now, var refers to content read out of the file (so nothing that you could close, would have a close method).

If you did:

open(from_file).close()

After (but also before; at any point), you would simply open that file (again) and close it immediately. BTW. this returns None, just in case you wanted to get the return value. But it would not affect previously open file handles and file-like objects. It would not serve any practical purpose except for perhaps making sure you can open a file.

But again. It's a good practice to perform the housekeeping, but strictly speaking (and esp. in a short code like this). If you did not close the file and relied on the OS to clean-up after your process. It'd work fine.

Upvotes: 2

Related Questions