2

I wrote this code and it prints rows sequentially:

with open(filename, 'r') as csvfile:
    rder = csv.reader(csvfile, delimiter=',')
    for row in rder:
        print(row)

Is it possible to access rder with hasNext and next fashion, i.e. without for construct? For example, how to skip first row?

The question is general, I would like to understand, what does it mean to be able to stay before in. In Java this means being of Iterable interface and I can easily find it's documentation and know, what I can do with it.

In Python there is no information that I can find about what is returned by the reader function and I don't know what I can do with it except what is written in example. And that example is written to only use it with for.

Can I choose to choose something else?

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
Dims
  • 47,675
  • 117
  • 331
  • 600
  • 1
    `rder` is an iterator. See: http://stackoverflow.com/questions/19151/build-a-basic-python-iterator – Stephen Rauch Mar 23 '17 at 16:59
  • How would I know, that `reader()` returns iterator? Usage in `for` implies this? – Dims Mar 23 '17 at 18:57
  • Yes, `for ... in X` implies `X` is an iterator of some sort. Python has a fairly rich variety of things which can be iterated over. – Stephen Rauch Mar 23 '17 at 19:02
  • 1
    `X` being usable in `for whatever in X` implies that `X` is an *iterable*. That might be an iterator (which wouldn't count as an iterable in Java), or it might be some sort of reusable iterable like a list or a Python 3 range object. – user2357112 Mar 23 '17 at 20:56
  • `rder` is not inherently an iterator; it is only an iterator because Python can use a text fie (and many other objects that return strings) as an iterator. – dawg Mar 25 '17 at 18:07

2 Answers2

3

csv.reader returns an iterator.

The iterator can be used in a for loop via in:

So this:

import csv
rder = csv.reader(my_csv(), delimiter=' ')
for row in rder:
    print(row)

gives:

['A', 'header', 'row']
['some', 'data']
['some', 'more', 'data']
['A', 'footer']

The iterator can be read inside the for loop using next():

If we need to consume a row inside of the for loop we can do:

rder = csv.reader(my_csv(), delimiter=' ')
for row in rder:
    print(row)
    print('Grabbed one:', next(rder))

To give:

['A', 'header', 'row']
Grabbed one: ['some', 'data']
['some', 'more', 'data']
Grabbed one: ['A', 'footer']

The iterator can also be used outside of the for loop:

rder = csv.reader(my_csv(), delimiter=' ')
print('first one:', next(rder))
print('second one:', next(rder))
for row in rder:
    print(row)

Will give:

first one: ['A', 'header', 'row']
second one: ['some', 'data']
['some', 'more', 'data']
['A', 'footer']

Test Data:

from io import StringIO

def my_csv():
    return StringIO('\n'.join([x.strip() for x in u"""
        A header row
        some data
        some more data
        A footer
    """.split('\n')[1:-1]]))
Graham
  • 7,431
  • 18
  • 59
  • 84
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
  • 1
    Bleh, SO Docs. Just looking at the first paragraph of what the link goes to, I can already see several mistakes. The [real Python docs](https://docs.python.org/3/) aren't the greatest, but they're usually better than that. – user2357112 Mar 23 '17 at 21:09
1

csv.reader returns an iterator based on the underlying objects iterator protocol. It will not work as an iterator if the underlying object does not support iteration.

If you use a text file, that is based on what is causing next to function for a text file (usually a \n sequence but not always...)

You can write a loop Python that is not based on the for protocol. Instead, you use try/except and catch the StopIteration exception:

with open('file.csv') as csvfile:
    rder = csv.reader(csvfile)
    while True:
        try:
            row=next(rder)  
            print row
        except StopIteration:
            break   

But specifically, to get a header, it is probably better to do:

with open('file.csv') as csvfile:
    rder = csv.reader(csvfile)
    header=next(rder)
    print header
    for row in rder:
        print row

Or, with Python3 you can shorten that to:

with open('/tmp/file.csv') as csvfile:
    rder = csv.reader(csvfile)
    header, *rows=[row for row in rder]

But you can also use a non-file object, like a string with csv and records separate by a separate delimiter:

>>> s='A,B,C|1,2,3|4,5,6'
>>> import csv
>>> for row in csv.reader(s.split("|")):
...    print row
... 
['A', 'B', 'C']
['1', '2', '3']
['4', '5', '6']

In this case, next is moving to the next list element that is the result of the .split()

dawg
  • 98,345
  • 23
  • 131
  • 206