16

I have this piece of code which creates a note and adds to the notebook. When I run this I get a Iteration over non-sequence error.

import datetime
class Note:
    def __init__(self, memo, tags):
        self.memo = memo
        self.tags = tags
        self.creation_date = datetime.date.today()

def __str__(self):
    return 'Memo={0}, Tag={1}'.format(self.memo, self.tags)


class NoteBook:
     def __init__(self):
        self.notes = []

     def add_note(self,memo,tags):
        self.notes.append(Note(memo,tags))

if __name__ == "__main__":
    firstnote = Note('This is my first memo','example')
    print(firstnote)
    Notes = NoteBook()
    Notes.add_note('Added thru notes','example-1')
    Notes.add_note('Added thru notes','example-2')
    for note in Notes:
        print(note.memo)

Error:

C:\Python27\Basics\OOP\formytesting>python notebook.py  
Memo=This is my first memo, Tag=example  
Traceback (most recent call last):  
  File "notebook.py", line 27, in   
    for note in Notes:  
TypeError: iteration over non-sequence
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
user1050619
  • 19,822
  • 85
  • 237
  • 413
  • 1
    You should provide the exact error message and where this occurs. The best way is to post the full trace back message. The more info you provide, to better the help you get. – Levon Aug 08 '12 at 19:19
  • As an FYI: you should explicitly inherit from object: `class NoteBook(object):`, this is what's known as a new-style class. See [this question|http://stackoverflow.com/questions/54867/old-style-and-new-style-classes-in-python] for details. – Adam Parkin Aug 08 '12 at 20:10

4 Answers4

13

You are trying to iterate over the object itself, which is returning the error. You want to iterate over the list inside the object, in this case Notes.notes (which is somewhat confusing naming, you may want to distinguish the internal list by using another name for the instance of the notebook object).

for note in Notes.notes:
    print(note.memo)
pk-nb
  • 2,850
  • 1
  • 18
  • 20
9

Notes is an instance of NoteBook. To iterate over such an object, it needs an __iter__ method:

class NoteBook:

    def __iter__(self):
        return iter(self.notes)

PS. It is a PEP8 recommendation/convention in Python to use lowercase variable names for instances of classes, and CamelCase for class names. Following this convention will help you instantly recognize your class instances from your classes.

If you wish to follow this convention (and endear yourself to others who like this convention), change Notes to notes.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Is there any benefit to doing the yield in `__iter__` vs just doing `return iter(self.notes)`? – Adam Parkin Aug 08 '12 at 20:07
  • Agreed with @AdamParkin. Why reinvent when you can just push the work off to an object that has it built in? See my answer for that implementation. – Jordan Aug 08 '12 at 20:35
  • 1
    possibly off-topic, and sorry to be pedantic, but what you have there is often called Pascal case: `NoteBook`, `MyClassName`, `JavaScript`. Camel case is where the first letter is lower-case, as in Java class names (iirc): `noteBook`, `myClassName`, `jQuery` – Yvonne Aburrow May 09 '17 at 10:23
2

If you wanted to actually iterate Notes itself, you could also add a custom __iter__ method to it that returns the .notes property.

class Notebook:

    def __iter__(self):
        return iter(self.notes)

    ...
Jordan
  • 31,971
  • 6
  • 56
  • 67
1

The problem is in the line for note in Notes: since Notes is an object not a list. I believe you want for note in Notes.notes:

also as unutbu pointed out, you can overload the __iter__ operator which will allow your current loop to work. It depends how you want this to outwardly appear. Personally I would overload __iter__

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174