1

I'm following an exercise in the book Learn Python the Hard Way. In the following code example, I'm opening, truncating and then writing to a file. When I try to use print target.read() without another repetitive target = open(filename) statement, I get an error message stating that it cannot list the contents of the script I'm currently working on because it's not open.

I'm wondering why do I need the repetitive target = open(filename) statement if I opened the file previously using the target = open(filename, 'w') statement.

from sys import argv

script, filename = argv

print "We're going to erase %r." % filename
print "If you don't want that, hit CTRL-C (^C)."
print "If you don't want that, hit RETURN."

raw_input("?")

print "Opening the file..."
target = open(filename, 'w')

print "Truncating the file.  Goodbye!"
target.truncate()

print "Now I'm going to ask you for three lines."

line1 = raw_input("line 1: ")
line2 = raw_input("line 2: ")
line3 = raw_input("line 3: ")

target.write(line1, "\n", line2, "\n", line3)

target = open(filename)
print target.read()

print "And finally, we close it."
target.close()
1
  • 1
    Please don't keep using LPTHW. It's awful. Learning Python 5th Ed. by Mark Lutz, or really anything else, is better. Commented Aug 2, 2015 at 2:13

2 Answers 2

3

EDIT 1 adds more information on file pointer operations.

The 'w' mode you used is for write-only.

To open a file for read/write access, you can use a file mode of 'r+' or 'rb+', but even if you do this, you will still have to rewind the file pointer to the start of the file using seek to read it after you write it.

f = open('filename', 'rb+')

The file has an associated file pointer (in the program's memory, not in the file itself) that defines where the next file operation will take place. If you're not careful, you can easily overwrite parts of the file you did not mean to.

To find out what the current pointer is, you can use the tell method of the file:

print(f.tell())

To change the current file pointer, you can use the seek method:

f.seek(0)

The seek method has an optional second parameter that lets you seek relative to the end of the file or relative to the current file pointer. By default, it seeks to an absolute position. Seeking to the end of the file is useful if you want to read some stuff from the file and then append to it:

f.seek(0, 2)  # Seeks past everything in the file

If you only want to append to a file without reading anything in it first, you can open it in append mode ('a' or 'ab'). This will open the file and seek to the end and be ready for writing. Using any other mode besides append will automatically seek to the beginning of the file when you open it.

If you have opened the file in text mode ('r+' rather than 'rb+') you can only seek to the beginning of the file. (Text mode is used to be able to support line endings from different operating systems on input. Opinions vary, but I think it is more trouble than it is worth, because it hides what is going on, and it is extremely easy to deal with different line endings from pure Python code.)

Sign up to request clarification or add additional context in comments.

4 Comments

Though I upvoted your answer (since it's correct) it would be good if you'll explain a bit further what do you mean by "seek" and "rewind" cause most chances are that the OP is not familiar with those terms.
OK, I will, though I always debate whether sending them off with a term is the best thing :-)
:) I see what you're saying. My personal preference is to send them off with enough knowledge + additional links to educate themselves even further.
Thanks for the thorough explanation. File mode is the next chapter so this helps me see how things are going to tie together.
3

This is terrible code to begin with, because it teaches you all sorts of bad habits, and is outdated to boot. When dealing with file objects, use the with statement and a context manager:

with open(filename, "w") as target:
    print "Truncating the file.  Goodbye!"
    target.truncate()

    print "Now I'm going to ask you for three lines."

    line1 = raw_input("line 1: ")
    line2 = raw_input("line 2: ")
    line3 = raw_input("line 3: ")

    target.write(line1, "\n", line2, "\n", line3)

# now we're out of the `with` block, and `target` has 
# been closed (and saved) automatically

# next block
with open(filename) as target:
    print target.read()

# and we're outside the block again, and `target` has
# been closed again, without having to call `.close()`
# explicitly. Isn't that easier to understand?

File objects get automatically closed (any any output written to them flushed to disk and saved before closure) when you exit the with block.

In the original code, target.close() was not called after .write(), but calling open() again on the same file with the same file handler name had the same effect. This is not intuitive, and is one reason why the Zen of Python (type import this into your Python interpreter) states "Explicit is better than implicit." While file closure is implicit in a with block, the very definition of that block says that the file will be closed when it ends.

7 Comments

I agree it's cleaner and more canonical and the right thing to learn and use at the end of the day. I'm a bit ambivalent about magical things like with statements and decorators for rank beginners though; not really sure if they slow understanding down or speed it up.
@PatrickMaupin why not? If you're learning to use a language, learn to use the language. I'm not saying start them on classes and metaprogramming on the first day, but with is an incredibly useful feature that helps prevent a lot of problems. When you exit the block, you're done dealing with that context manager (open() in this case). Pure and simple.
Here's a guy who says of the with statement "context managers completely solve this problem, but often when teaching complete beginners I want to introduce basic file IO before we start dealing with indented blocks." Whether coincidence or not, the original example above has no indented blocks...
@Patrick you can find anyone with any opinion online. It's related to Rule 34, I think :) Seriously, though, there are a lot of different approaches to teaching, and many different ways of learning. Yes, I think basic I/O should be understood before context managers, but the problem with the OP's code via LPTHW is that proper I/O wasn't being used, at least there. What's the point of tricking someone by omitting a .close() call, then calling .open() again? I just think it's bad form. It's my opinion, and some may disagree, but I think it's reasonable.
I don't think you saw me disagreeing about the close statement :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.