60

I'm trying to write an array (list?) to a text file using Python 3. Currently I have:

def save_to_file(*text):

    with open('/path/to/filename.txt', mode='wt', encoding='utf-8') as myfile:
        for lines in text:
            print(lines, file = myfile)
    myfile.close

This writes what looks like the array straight to the text file, i.e.,

['element1', 'element2', 'element3']
username@machine:/path$

What I'm looking to do is create the file with

element1
element2
element3
username@machine:/path$

I've tried different ways to loop through and append a "\n" but it seems that the write is dumping the array in one operation. The question is similar to How to write list of strings to file, adding newlines? but the syntax looked like it was for Python 2? When I tried a modified version of it:

def save_to_file(*text):

    myfile = open('/path/to/filename.txt', mode='wt', encoding='utf-8')
    for lines in text:
        myfile.write(lines)
    myfile.close

...the Python shell gives "TypeError: must be str, not list" which I think is because of changes between Python2 and Python 3. What am I missing to get each element on a newline?

EDIT: Thank you to @agf and @arafangion; combining what both of you wrote, I came up with:

def save_to_file(text):

    with open('/path/to/filename.txt', mode='wt', encoding='utf-8') as myfile:
        myfile.write('\n'.join(text))
        myfile.write('\n')

It looks like I had part of the issue with "*text" (I had read that expands arguments but it didn't click until you wrote that [element] was becoming [[element]] that I was getting a str-not-list type error; I kept thinking I needed to tell the definition that it was getting a list/array passed to it and that just stating "test" would be a string.) It worked once I changed it to just text and used myfile.write with join, and the additional \n puts in the final newline at the end of the file.

Community
  • 1
  • 1
Bart Silverstrim
  • 3,445
  • 6
  • 34
  • 44
  • There are many bugs in this code, one of them being `myfile.close` does not actually call the function,` myfile.close()`does. But anyway you shouldn't be explicitly calling it when you're also using `with`. – smci Jul 01 '22 at 22:52

3 Answers3

89

myfile.close -- get rid of that where you use with. with automatically closes myfile, and you have to call close like close() anyway for it to do anything when you're not using with. You should just always use with on Python 3.

with open('/path/to/filename.txt', mode='wt', encoding='utf-8') as myfile:
    myfile.write('\n'.join(lines))

Don't use print to write to files -- use file.write. In this case, you want to write some lines with line breaks in between, so you can just join the lines with '\n'.join(lines) and write the string that is created directly to the file.

If the elements of lines aren't strings, try:

    myfile.write('\n'.join(str(line) for line in lines))

to convert them first.

Your second version doesn't work for a different reason. If you pass

['element1', 'element2', 'element3']

to

def save_to_file(*text):

it will become

[['element1', 'element2', 'element3']]

because the * puts each argument it gets into a list, even if what you pass is already a list.

If you want to support passing multiple lists, and still write them one after another, do

def save_to_file(*text):

    with open('/path/to/filename.txt', mode='wt', encoding='utf-8') as myfile:
        for lines in text:
            myfile.write('\n'.join(str(line) for line in lines))
            myfile.write('\n')

or, for just one list, get rid of the * and do what I did above.

Edit: @Arafangion is right, you should probably just use b instead of t for writing to your files. That way, you don't have to worry about the different ways different platforms handle newlines.

agf
  • 171,228
  • 44
  • 289
  • 238
  • 1
    I would suggest that you always use with regardless of whether you're using python3 or python2. It's common syntax. – Arafangion Aug 21 '11 at 14:06
  • I suggest you incorporate part (or all) of my answer in this answer, then there should be a "Good answer" for this question, just be aware that 'text' here refers to the *arguments*, so it is always a list. – Arafangion Aug 21 '11 at 14:08
  • You can't always use `with` in PYthon 2, if you need 2.5 or older compatibility. – agf Aug 21 '11 at 14:25
  • Python 2.5 or older is very old, but yes, it's still used in some places. As for utf-8 encoding, how is that related to 'text mode'? That only asks the operating system to mangle his '\n''s into '\r\n' or whatever automatically. – Arafangion Aug 21 '11 at 14:28
  • And thank you for the explanations. I'm trying to learn the language and wish to understand why it wasn't working, and your answers both helped. – Bart Silverstrim Aug 21 '11 at 14:32
  • I think the majority of places using Python in production are still on 2.4-2.6. App engine, for example, is still on 2.5, though they're soon moving to 2.7, and it works fine to `from __future__ import with_statement` in GAE code. – agf Aug 21 '11 at 14:34
  • @agf: I tried using the b instead of t (and eliminating the encoding part) but then it complained that 'str' doesn't support the buffer interface. Maybe it has to be converted into a bytes()? – Bart Silverstrim Aug 21 '11 at 15:03
  • Yes, if you're on Python 3, you need to either `str.encode()` or use `t`. – agf Aug 21 '11 at 16:16
2

There are numerous mistakes there.

  1. Your indentation is messed up.
  2. The 'text' attribute in save_to_file refers to ALL the arguments, not just the specific argument.
  3. You're using "text mode", which is also confusing. Use binary mode instead, so that you have a consistent and defined meaning for '\n'.

When you iterate over the 'lines in text', because of these mistakes, what you're really doing is iterating over your arguments in the function, because 'text' represents all your arguments. That is what '*' does. (At least, in this situation. Strictly speaking, it iterates over all the remaining arguments - please read the documentation).

Arafangion
  • 11,517
  • 1
  • 40
  • 72
  • I think the indentation is just a code formatting error in the question, not in the original code. – agf Aug 21 '11 at 14:10
  • Just saw that; wondered how it was running with the incorrect indenting :-) yes, it was from the question posted here, the actual code was indented properly. I should have caught that. – Bart Silverstrim Aug 21 '11 at 14:22
0

I'm a bigginner in python but found this soloution and worked for me. By using a "f" string each item in a list can be written in a new line in a text file.

my_list=['a', 'b', 'c']

with open('/path/to/filename.txt', mode='w', encoding='utf-8') as myfile:

for item in my_list:

myfile.write(f"\n{item}")

output:

a

b

c

Hassan
  • 1
  • 1