user1367204
user1367204

Reputation: 4797

Why won't my class that returns a list iterate?

Here is my code, I use it to open an excel sheet and then return each row as a list of strings (where each cell is a string). The class returns one list that is filled with as many lists as there are rows in the file. So 50 rows will return 50 lists.

from xlrd import open_workbook

class ExcelReadLines(object):

    def __init__(self,path_to_file):
        '''Accepts the Excel File'''
        self.path_to_file = path_to_file
        self.__work__()


    def __work__(self):
        self.full_file_as_read_lines = []
        self.book = open_workbook(self.path_to_file)
        self.sheet = self.book.sheet_by_index(0)

        for row_index in range(self.sheet.nrows):
            single_read_lines = []
            for col_index in range(self.sheet.ncols):
                cell_value_as_string = str(self.sheet.cell(row_index,col_index).value)
                cell_value_stripped = cell_value_as_string.strip('u')
                single_read_lines.append(cell_value_stripped)
            self.full_file_as_read_lines.append(single_read_lines)

        return self.full_file_as_read_lines

But when I run:

for x in ExcelReader('excel_sheet'): print x

I get the error message:

class is not iterable

Upvotes: 1

Views: 167

Answers (4)

Jon Clements
Jon Clements

Reputation: 142176

Unless I'm mistaken, what you're really after is

def first_sheet(fname):
    wb = xlrd.open_workbook(fname)
    ws = wb.sheet_by_index(0)
    for i in xrange(ws.nrows):
        yield ws.row_values(i) # maybe strip 'u''s - but that looks a bit sus... (probably something to do with your `str`)

list_of_rows = list(first_sheet('somefile.xls'))

Then do any transposition using zip if needs be...

Upvotes: 0

mgilson
mgilson

Reputation: 309939

In order for a class to be iterable, it needs to have an __iter__ method.

Consider:

class Foo(object):
    def __init__(self,lst):
        self.lst = lst
    def __iter__(self):
        return iter(self.lst)

example:

>>> class Foo(object):
...     def __init__(self,lst):
...         self.lst = lst
...     def __iter__(self):
...         return iter(self.lst)
... 
>>> Foo([1,2,3])
<__main__.Foo object at 0xe9890>
>>> for x in Foo([1,2,3]): print x
... 
1
2
3

Your example seems like it would be a good bit better as a generator -- I don't really understand what the need is for a class here:

def excel_reader(path_to_file):
    book = open_workbook(path_to_file)
    sheet = book.sheet_by_index(0)

    for row_index in range(sheet.nrows):
        single_read_lines = []
        for col_index in range(sheet.ncols):
            cell_value_as_string = str(self.sheet.cell(row_index,col_index).value)
            cell_value_stripped = cell_value_as_string.strip('u')
            single_read_lines.append(cell_value_stripped)
        yield single_read_lines

Upvotes: 7

BrenBarn
BrenBarn

Reputation: 251408

You have a few problems here.

  1. Your code doesn't return anything. You call __work__ but don't return the value.

  2. Even if it did, that wouldn't help, because returning something from __init__ doesn't make the object be that thing.

  3. You don't want your object to be a list anyway, you just want to iterate over it.

See this question for a simple example of how to write an iterator in Python.

In addition, you shouldn't use double-underscore-sandwich names like __work__ in your code. That sort of name is by convention reserved for Python-internal use.

Upvotes: 1

Chris Laplante
Chris Laplante

Reputation: 29658

You should look into implementing Python's special iterator methods.

Also, note that you shouldn't name a method __work__ since it uses magic method syntax but isn't actually a real magic method.

Upvotes: 2

Related Questions