简体   繁体   中英

File close error, [AttributeError: 'int' object has no attribute 'close'] when reducing file write code to a single line

Going through Zed Shaw's book Exercise 17 [about copying one file to another] where he reduces his these two lines of code

in_file = open(from_file)
indata = in_file.read()

into one as :

indata = open(from_file).read()

there's also a piece of code where he writes

out_file = open(to_file, 'w')
out_file.write(indata)

So I was reducing this into one line same as above :

out_file = open(to_file, 'w').write(indata)

This seems to work fine but when I close the out_file there's an error as:

Traceback (most recent call last):
  File "filesCopy.py", line 27, in <module>
    out_file.close()
AttributeError: 'int' object has no attribute 'close'

I am unable to grasp what is going on and how close() is working here?

The write method returns the number of characters written in your file which is an integer not a file object and therefore doesn't have a close method.

In [6]: a = open('test', 'w')          
In [7]: t = a.write('ssss')
In [8]: t
Out[8]: 4

Also, calling the I/O methods directly on open() is recommended only if you don't want to have any further interaction with the file. Besides, the most proper way to deal with file objects is to use a with statement that closes the file automatically at the end of the block and there's no need to call the close() manually.

with open('filename', 'w') as f:
    # do something

Following is usually better approach, both for reading & writing:

with open("myfile.txt", "w") as f:
    # do something with f

There is no need to close f with this code.

With code val = open(to_file, 'w').write(indata) "val" will be return value of write function, not open function.

The two are not equivalent. If you write out_file = open(to_file, 'w').write(indata) , you have implicitly written:

# equivalent to second code sample
temp = open(to_file, 'w')
out_file = temp.write(indata)

Now as we can see in the documentation of write() :

f.write(string) writes the contents of string to the file, returning the number of characters written .

So it returns an integer. So in your second sample out_file is not a file handler, but an integer. Further in the code, you somewhere aim to close the out_file file handler with out_file.close() . But since out_file is no longer a file handler, it thus makes no sense to call close on this.

Nevertheless, by using a context, you do no longer need to perform a .close() yourself, so more elegantly is probably:

with open(to_file, 'w') as out_file:
    out_file.write(indata)

The reduction in the book itself is allowed (well at least semantically, it is better to use context manager), since the author probably never closes the file handle explicitly.

The read() function reads the file and then returns the file content, so when you assign indata to read() it's assigning the returned file content. The difference is that the write() function returns the number of characters written, therefore your outdata is now an int object.

Read more about it here: https://docs.python.org/3.6/tutorial/inputoutput.html

Therefore, you cannot combine the write() function into one line and expect to have a referenced file object to close after, and that's disastrous.

The preferred way is to use the with block. Refer to @Willem's answer for more details.

The author "Zed" has already clarified when you will read the page 64 from that book. Please read below:

When I try to make this script shorter, I get an error when I close the files at the end. You probably did something like this, indata = open(from_file).read(), which means you don't need to then do in_file.close() when you reach the end of the script. It should already be closed by Python once that one line runs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM