Python - 'str' object has no attribute 'close' - python

I am having a great time trying to figure out why there doesn't need to be a closing attribute for this few lines of code I wrote:
from sys import argv
from os.path import exists
script, from_file, to_file = argv
file_content = open(from_file).read()
new_file = open(to_file, 'w').write(file_content)
new_file.close()
file_content.close()
I read some things and other people's posts about this, but their scripts were a lot more complicated than what I'm currently learning, so I couldn't figure out why.
I am doing Learning Python the Hard Way and would appreciate any help.

file_content is a string variable, which contains contents of the file -- it has no relation to the file. The file descriptor you open with open(from_file) will be closed automatically: file sessions are closed after the file-objects exit the scope (in this case, immediately after .read()).

open(...) returns a reference to a file object, calling read on that reads the file returning a string object, calling write writes to it returning None, neither of which have a close attribute.
>>> help(open)
Help on built-in function open in module __builtin__:
open(...)
open(name[, mode[, buffering]]) -> file object
Open a file using the file() type, returns a file object. This is the
preferred way to open a file.
>>> a = open('a', 'w')
>>> help(a.read)
read(...)
read([size]) -> read at most size bytes, returned as a string.
If the size argument is negative or omitted, read until EOF is reached.
Notice that when in non-blocking mode, less data than what was requested
may be returned, even if no size parameter was given.
>>> help(a.write)
Help on built-in function write:
write(...)
write(str) -> None. Write string str to file.
Note that due to buffering, flush() or close() may be needed before
the file on disk reflects the data written.
Theres a couple ways of remedying this:
>>> file = open(from_file)
>>> content = file.read()
>>> file.close()
or with python >= 2.5
>>> with open(from_file) as f:
... content = f.read()
The with will make sure the file is closed.

When you do file_content = open(from_file).read(), you set file_content to the contents of the file (as read by read). You can't close this string. You need to save the file object separately from its contents, something like:
theFile = open(from_file)
file_content = theFile.read()
# do whatever you need to do
theFile.close()
You have a similar problem with new_file. You should separate the open(to_file) call from the write.

Related

Writing and reading at the same time in Python?

I'm a total beginner in Python. I've been trying to shorten the #1 version (which works fine) to a cleaner code and I thought I could just squeeze it into a one-liner. Why doesn't the #2 work?
I'm getting the "NoneType" object has no attribute 'seek' when I try to run it.
from sys import argv
script, filename = argv
# 1
open_file = open(filename, 'w+')
open_file.write("Hello world!")
open_file.seek(0)
print open_file.read()
# 2
open_file = open(filename, 'w+').write("Hello world!").seek(0).read()
print open_file
I have tried numerous ways but I still can't get it to work.
Thanks a lot!
From the documentation:
write(b):
Write the given bytes-like object, b, to the underlying raw stream, and return the number of bytes written
So, it returns the number of bytes written, not a file object, so you cannot chain another call after the write(). Note you also cannot chain seek() as that returns an offset into the file.
The reason why a one-liner is not working, is because .write() does not return back the original file descriptor. Instead, write() returns the number of characters/bytes (depending on the opened file mode) that have been written.
Unfortunatelly I don't think there's a way to do what you want in one go, that would not sacrifice readability. What you could do is wrap your code into a function, or write your own file-like object that would immediatelly seek after write.
For writing,
with open(filename, 'w+') as f: f.write("Hello world!")
and to print content
with open(filename, 'r+') as f: print(f.read())
works for me! Nonetype error is because of no return type as pointed out by others.
You are getting NoneType because file.write function return None. Or in this case int (How many bytes it has write on the file)
You must close the file before you open again it. open_file.close()

TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper

I am trying to open, read, modify, and close a json file using the example here:
How to add a key-value to JSON data retrieved from a file with Python?
import os
import json
path = '/m/shared/Suyash/testdata/BIDS/sub-165/ses-1a/func'
os.chdir(path)
string_filename = "sub-165_ses-1a_task-cue_run-02_bold.json"
with open ("sub-165_ses-1a_task-cue_run-02_bold.json", "r") as jsonFile:
json_decoded = json.load(jsonFile)
json_decoded["TaskName"] = "CUEEEE"
with open(jsonFile, 'w') as jsonFIle:
json.dump(json_decoded,jsonFile) ######## error here that open() won't work with _io.TextIOWrapper
I keep getting an error at the end (with open(jsonFile...) that I can't use the jsonFile variable with open(). I used the exact format as the example provided in the link above so I'm not sure why it's not working. This is eventually going in a larger script so I want to stay away from hard coding/ using strings for the json file name.
This Question is a bit old, but for anyone with the same issue:
You're right you can't open the jsonFile variable. Its a pointer to another file connection and open wants a string or something similar. Its worth noting that jsonFile should also be closed once you exit the 'with' block so it should not be referenced outside of that.
To answer the question though:
with open(jsonFile, 'w') as jsonFile:
json.dump(json_decoded,jsonFile)
should be
with open(string_filename, 'w') as jsonFile:
json.dump(json_decoded,jsonFile)
You can see we just need to use the same string to open a new connection and then we can give it the same alias we used to read the file if we want. Personally I prefer in_file and out_file just to be explicit about my intent.

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 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.
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 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
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.

Use of python close command (LPTHW ex 17 extra credit) [duplicate]

I am having a great time trying to figure out why there doesn't need to be a closing attribute for this few lines of code I wrote:
from sys import argv
from os.path import exists
script, from_file, to_file = argv
file_content = open(from_file).read()
new_file = open(to_file, 'w').write(file_content)
new_file.close()
file_content.close()
I read some things and other people's posts about this, but their scripts were a lot more complicated than what I'm currently learning, so I couldn't figure out why.
I am doing Learning Python the Hard Way and would appreciate any help.
file_content is a string variable, which contains contents of the file -- it has no relation to the file. The file descriptor you open with open(from_file) will be closed automatically: file sessions are closed after the file-objects exit the scope (in this case, immediately after .read()).
open(...) returns a reference to a file object, calling read on that reads the file returning a string object, calling write writes to it returning None, neither of which have a close attribute.
>>> help(open)
Help on built-in function open in module __builtin__:
open(...)
open(name[, mode[, buffering]]) -> file object
Open a file using the file() type, returns a file object. This is the
preferred way to open a file.
>>> a = open('a', 'w')
>>> help(a.read)
read(...)
read([size]) -> read at most size bytes, returned as a string.
If the size argument is negative or omitted, read until EOF is reached.
Notice that when in non-blocking mode, less data than what was requested
may be returned, even if no size parameter was given.
>>> help(a.write)
Help on built-in function write:
write(...)
write(str) -> None. Write string str to file.
Note that due to buffering, flush() or close() may be needed before
the file on disk reflects the data written.
Theres a couple ways of remedying this:
>>> file = open(from_file)
>>> content = file.read()
>>> file.close()
or with python >= 2.5
>>> with open(from_file) as f:
... content = f.read()
The with will make sure the file is closed.
When you do file_content = open(from_file).read(), you set file_content to the contents of the file (as read by read). You can't close this string. You need to save the file object separately from its contents, something like:
theFile = open(from_file)
file_content = theFile.read()
# do whatever you need to do
theFile.close()
You have a similar problem with new_file. You should separate the open(to_file) call from the write.

Open/write deleting txt file contents?

I am bassicly trying to read a number from a file, convert it to an int, add one to it, then rewrite the new number back to the file. However every time I run this code when i open the .txt file it is blank. Any help would be appreciated thanks! I am a python newb.
f=open('commentcount.txt','r')
counts = f.readline()
f.close
counts1 = int(counts)
counts1 = counts1 + 1
print(counts1)
f2 = open('commentcount.txt','w') <---(the file overwriting seems to happen here?)
f2.write(str(counts1))
Having empty files
This issue is caused by you failing to close the file descriptor. You have f.close but it should be f.close() (a function call). And you also need an f2.close() in the end.
Without the close it takes a while until the contents of the buffer arrive in the file. And it is a good practice to close file descriptors as soon as they are not used.
As a side note, you can use the following syntactic sugar to ensure that the file descriptor is closed as soon as possible:
with open(file, mode) as f:
do_something_with(f)
Now, regarding the overwriting part:
Writing to file without overwriting the previous content.
Short answer: You don't open the file in the proper mode. Use the append mode ("a").
Long answer:
It is the intended behavior. Read the following:
>>> help(open)
Help on built-in function open in module __builtin__:
open(...)
open(name[, mode[, buffering]]) -> file object
Open a file using the file() type, returns a file object. This is the
preferred way to open a file. See file.__doc__ for further information.
>>> print file.__doc__
file(name[, mode[, buffering]]) -> file object
Open a file. The mode can be 'r', 'w' or 'a' for reading (default),
writing or appending. The file will be created if it doesn't exist
when opened for writing or appending; it will be truncated when
opened for writing. Add a 'b' to the mode for binary files.
Add a '+' to the mode to allow simultaneous reading and writing.
If the buffering argument is given, 0 means unbuffered, 1 means line
buffered, and larger numbers specify the buffer size. The preferred way
to open a file is with the builtin open() function.
Add a 'U' to mode to open the file for input with universal newline
support. Any line ending in the input file will be seen as a '\n'
in Python. Also, a file so opened gains the attribute 'newlines';
the value for this attribute is one of None (no newline read yet),
'\r', '\n', '\r\n' or a tuple containing all the newline types seen.
So, reading the manuals shows that if you want the content to be kept you should open in append mode:
open(file, "a")
you should use the with statement. this assume that the file descriptor is closed no matter what:
with open('file', 'r') as fd:
value = int(fd.read())
with open('file', 'w') as fd:
fd.write(value + 1)
You never close the file. If you don't properly close the file the OS might not commit any changes. To avoid this problem it is recommended that you use Python's with statement to open files as it it will close them for you once you are done with the file.
with open('my_file.txt', a) as f:
do_stuff()
python open file paramters:
w:
Opens a file for writing only. Overwrites the file if the file exists.
If the file does not exist, creates a new file for writing.
You can use a (append):
Opens a file for appending. The file pointer is at the end of the file
if the file exists. That is, the file is in the append mode. If the
file does not exist, it creates a new file for writing.
for more information you can read here
One more advice is to use with:
with open("x.txt","a") as f:
data = f.read()
............
For example:
with open('c:\commentcount.txt','r') as fp:
counts = fp.readline()
counts = str(int(counts) + 1)
with open('c:\commentcount.txt','w') as fp:
fp.write(counts)
Note this will work only if you have a file name commentcount and it has a int at the first line since r does not create new file, also it will be only one counter...it won't append a new number.

Categories