155

So I'm learning Python. I am going through the lessons and ran into a problem where I had to condense a great many target.write() into a single write(), while having a "\n" between each user input variable(the object of write()).

I came up with:

nl = "\n"
lines = line1, nl, line2, nl, line3, nl
textdoc.writelines(lines)

If I try to do:

textdoc.write(lines)

I get an error. But if I type:

textdoc.write(line1 + "\n" + line2 + ....)

Then it works fine. Why am I unable to use a string for a newline in write() but I can use it in writelines()?

Python 2.7

martineau
  • 119,623
  • 25
  • 170
  • 301
AbeLinkon
  • 1,675
  • 2
  • 12
  • 9

5 Answers5

194
  • writelines expects an iterable of strings
  • write expects a single string.

line1 + "\n" + line2 merges those strings together into a single string before passing it to write.

Note that if you have many lines, you may want to use "\n".join(list_of_lines).

Jab
  • 26,853
  • 21
  • 75
  • 114
DGH
  • 11,189
  • 2
  • 23
  • 24
  • 11
    Why should you use `write` instead of `writelines` if you have many lines? Writelines could be better performing as it doesn't have to create a temporary concatenated string, just iterating over the lines. – Bouke Jan 15 '14 at 10:52
  • 1
    @hBy2Py: exactly the opposite: http://stackoverflow.com/a/6165711/281545 – Mr_and_Mrs_D Mar 11 '17 at 15:11
  • 4
    A single string is also an iterable in Python – natbusa Nov 08 '19 at 03:15
150

Why am I unable to use a string for a newline in write() but I can use it in writelines()?

The idea is the following: if you want to write a single string you can do this with write(). If you have a sequence of strings you can write them all using writelines().

write(arg) expects a string as argument and writes it to the file. If you provide a list of strings, it will raise an exception (by the way, show errors to us!).

writelines(arg) expects an iterable as argument (an iterable object can be a tuple, a list, a string, or an iterator in the most general sense). Each item contained in the iterator is expected to be a string. A tuple of strings is what you provided, so things worked.

The nature of the string(s) does not matter to both of the functions, i.e. they just write to the file whatever you provide them. The interesting part is that writelines() does not add newline characters on its own, so the method name can actually be quite confusing. It actually behaves like an imaginary method called write_all_of_these_strings(sequence).

What follows is an idiomatic way in Python to write a list of strings to a file while keeping each string in its own line:

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.write('\n'.join(lines))

This takes care of closing the file for you. The construct '\n'.join(lines) concatenates (connects) the strings in the list lines and uses the character '\n' as glue. It is more efficient than using the + operator.

Starting from the same lines sequence, ending up with the same output, but using writelines():

lines = ['line1', 'line2']
with open('filename.txt', 'w') as f:
    f.writelines("%s\n" % l for l in lines)

This makes use of a generator expression and dynamically creates newline-terminated strings. writelines() iterates over this sequence of strings and writes every item.

Edit: Another point you should be aware of:

write() and readlines() existed before writelines() was introduced. writelines() was introduced later as a counterpart of readlines(), so that one could easily write the file content that was just read via readlines():

outfile.writelines(infile.readlines())

Really, this is the main reason why writelines has such a confusing name. Also, today, we do not really want to use this method anymore. readlines() reads the entire file to the memory of your machine before writelines() starts to write the data. First of all, this may waste time. Why not start writing parts of data while reading other parts? But, most importantly, this approach can be very memory consuming. In an extreme scenario, where the input file is larger than the memory of your machine, this approach won't even work. The solution to this problem is to use iterators only. A working example:

with open('inputfile') as infile:
    with open('outputfile') as outfile:
        for line in infile:
            outfile.write(line)

This reads the input file line by line. As soon as one line is read, this line is written to the output file. Schematically spoken, there always is only one single line in memory (compared to the entire file content being in memory in case of the readlines/writelines approach).

Dr. Jan-Philip Gehrcke
  • 33,287
  • 14
  • 85
  • 130
  • 1
    Thank you for this very detailed answer. Based on your explanation I would assume that f.writelines() would be more useful in generating a log file and f.write() is more useful for single or multiple edits in a particular or multiple files? – AbeLinkon Sep 11 '12 at 23:27
  • 10
    @AbeLinkon: I would not support this conclusion. `write()` and `writelines()` are basically equivalent and their usage also is a question of personal taste. However, it is important to note that for an extremely long list of strings (called `lines`) it is less efficient to write `f.write('\n'.join(lines))` than `for l in line: f.write('%s\n' % l)`. In the first case, an entirely new and very long string is created in memory before writing it. In the second case the data is written piece-wise. – Dr. Jan-Philip Gehrcke Sep 12 '12 at 08:52
  • 4
    f.write('\n'.join(lines)) did not add the last nl when I ran it. – Jiminion Jul 26 '13 at 20:37
  • 1
    @Jim: this is to be expected, `'s'.join(iterable)` only fills the gaps with 's', it does not add prefix/suffix. – Dr. Jan-Philip Gehrcke Jul 26 '13 at 23:46
  • 6
    Of course you wouldn't do `outf.writelines(inf.readlines())` but rather `outf.writelines(inf)`. The function we don't want to use anymore is `readlines()` not `writelines()`. – moooeeeep May 20 '15 at 06:35
  • 2
    @moooeeeep: while nothing is wrong with the functionality/implementation of `writelines()`, its semantics are, as explained, less than ideal. This is why I have never used it. And I never missed it. – Dr. Jan-Philip Gehrcke Aug 26 '15 at 19:52
  • 2
    @AbeLinkon - maybe you should consider to accept this answer, it is clearly better than the one you originally accepted – Peter M. - stands for Monica Apr 27 '16 at 20:23
  • 1
    @Jan-PhilipGehrcke, this would be clearer with a trailing newline: ``f.write('\n'.join(lines) + '\n')`` so that it matches ``f.writelines("%s\n" % l for l in lines)`` in the second snippet – galath Jul 19 '17 at 10:10
-7

Actually, I think the problem is that your variable "lines" is bad. You defined lines as a tuple, but I believe that write() requires a string. All you have to change is your commas into pluses (+).

nl = "\n"
lines = line1+nl+line2+nl+line3+nl
textdoc.writelines(lines)

should work.

Kevin
  • 708
  • 2
  • 9
  • 20
-7

Exercise 16 from Zed Shaw's book? You can use escape characters as follows:

paragraph1 = "%s \n %s \n %s \n" % (line1, line2, line3)
target.write(paragraph1)
target.close()
Abhijeet Kasurde
  • 3,937
  • 1
  • 24
  • 33
Gerald
  • 1
  • Very weak solution. If you wanted to concatenate several lines in this manner, you should do it like this: `" \n ".join((line1, line2, line3))`. – Bachsau Jul 31 '19 at 15:52
-8

if you just want to save and load a list try Pickle

Pickle saving:

with open("yourFile","wb")as file:
 pickle.dump(YourList,file)

and loading:

with open("yourFile","rb")as file:
 YourList=pickle.load(file)
Venya
  • 190
  • 7