LambdaLabTM
Computer Science · Class 12 · CSV Files
CSV fileswriting⏱️ 10 min read

writerows()

One letter longer than writerow(), and it does the loop for you. Hand it a list of rows and every one of them lands in the file. The whole lesson is that word of: a list of lists, not a list of values — and getting that wrong produces no error at all.

1writerows() — a whole table at once

w.writerows(a list of lists)

Each inner list becomes one row. It is writerow() in a loop, written for you.

write_all.py
import csv

students = [[1, 'Ravi', 78], [2, 'Meera', 91], [3, 'Amit', 65]]

f = open('marks.csv', 'w', newline='')
w = csv.writer(f)

w.writerow(['Roll', 'Name', 'Marks'])
w.writerows(students)

f.close()
print(open('marks.csv', newline='').read())
Output
Roll,Name,Marks
1,Ravi,78
2,Meera,91
3,Amit,65
w.writerow(['Roll', 'Name', 'Marks'])

One row, so writerow — singular. The header is a single list of three values.

w.writerows(students)

Three rows, so writerows — plural. students is a list holding three lists, and each of those becomes a line.

no for loop anywhere

That is the whole gain. The three lines of loop from the previous lesson collapse into this one call.

2It is exactly the loop you would have written

These two halves produce the same file, byte for byte. Neither one is more correct — use whichever reads better:

the loop
for s in students:
    w.writerow(s)

Use this when each row needs checking or changing before it goes in.

one call
w.writerows(students)

Use this when the list is already exactly what you want in the file.

3The trap: a list that is one level too shallow

writerows() expects each item to be a row, and a row is a list of values. Hand it a list of strings and it does not complain — it treats each string as a row, and each letter as a value:

rows_trap.py
import csv

f = open('trap.csv', 'w', newline='')
w = csv.writer(f)
w.writerows(['Ravi', 'Meera', 'Amit'])
f.close()

print(open('trap.csv', newline='').read())
Output
R,a,v,i
M,e,e,r,a
A,m,i,t
No error, and the file is nonsense
This is the most dangerous kind of bug: the program runs, prints nothing alarming, and writes a file you will not look at until much later. When a CSV comes out with one letter per column, this is why.

Numbers do not even get that far, because a number cannot be taken apart into values:

rows_trap2.py
import csv

f = open('trap2.csv', 'w', newline='')
w = csv.writer(f)
w.writerows([1, 2, 3])
f.close()
Output
Traceback (most recent call last):
  File "rows_trap2.py", line 5, in <module>
    w.writerows([1, 2, 3])
_csv.Error: iterable expected, not int

“Iterable” is Python's word for something you can loop over — a list, or a string. A number is not one, so this time it stops. What you almost certainly meant was three rows of one value each:

fixed.py
import csv

f = open('fixed.csv', 'w', newline='')
w = csv.writer(f)
w.writerows([[1], [2], [3]])
f.close()

print(open('fixed.csv', newline='').read())
Output
1
2
3

4Counting the brackets

The rule is easier to see than to say. Count the opening brackets:

w.writerow(['Ravi', 78])one [

One row. writerow, singular.

w.writerows([['Ravi', 78], ['Meera', 91]])two [[

Rows inside a list. writerows, plural.

w.writerows(['Ravi', 78])one [

One bracket with writerows — the shapes do not match. 'Ravi' becomes R,a,v,i and 78 raises _csv.Error.

Key Takeaway
writerow takes one row. writerows takes rows. The s on the end tells you there is one more layer of square brackets than you would otherwise need.

5Try both, and break one on purpose

rows_playground.py

6The two, side by side

Let's Recap!
writerow()writerows()
Takesone list — a single rowa list of lists — many rows
Writesone lineone line per inner list
Typical usethe header, or a row from input()a table you already have in a variable
Gives backthe number of characters writtenNone
If you pass a plain stringone letter per columnone letter per column, on every row

7Recap

w.writerows(rows)

Writes every row in one call. Nothing else is different — it is writerow() in a loop.

A list OF lists

Each item must itself be a row. Two opening brackets where writerow had one.

A flat list of strings runs anyway

R,a,v,i on its own line, one per string, and no error to warn you.

A flat list of numbers stops

_csv.Error: iterable expected, not int — a number cannot be taken apart into values.

It returns None

Unlike writerow(), which returns a character count. Neither is worth catching.

The header still needs writerow()

It is one row, written once, above the writerows() call.

✍️ Now write these yourself
  1. 1

    Build a list of five rows in a variable and write the whole thing with one writerows().

    Hint · A header with writerow() first, then the table.

  2. 2

    Rewrite the same program with a for loop and writerow(), and compare the files.

    Hint · Identical. Pick whichever you find easier to read.

  3. 3

    Pass writerows() a flat list of names and look at the file.

    Hint · One letter per column, and Python said nothing.

  4. 4

    Pass it a flat list of numbers and read the error carefully.

    Hint · Iterable means “something you can loop over”.

Quick Check

What does writerows() expect as its argument?

Quick Check

w.writerows(['Ravi', 'Meera']) does what?

Quick Check

Why does w.writerows([1, 2, 3]) raise an error when the string version does not?

Quick Check

Which writes a header row followed by three records?