views:

70

answers:

4

Hey everyone,

So I've done some looking online and throught the documentation, but I'm having troubling finding out how to do this. I am working on creating an adventure game. I have a level class (which contains a bunch of rooms) and a Room class. I would like to be able to do something like the following.

l = Level()
for room in l:
    #following code here

This seems pretty simple to me, however I can't find the correct procedures for implementing a custom iterator. This may be an easy answer, but I'm having trouble finding it online. Any help is greatly appreciated!

+2  A: 

Use __iter__ with an iterator-generator. E.g.

def __iter__(self):
  for r in rooms:
    yield r

An iterator-generator is basically a psuedo-method used to implement an iterator. Note that there is no requirement that the generator use a for loop. It can use any combination of constructs (if, for, while, etc.) as needed. Basically, you just have to remember that the caller will get elements in the order you "call" yield, and the iteration will end when the method does.

See this section of the Python tutorial.

Matthew Flaschen
My problem with this answer (the other answer too, but less so) is really that you never explain that this makes the class instances *iterables*, not *iterators*. In fact you seem to really confuse the issue and the casual reader might think that this would make the instances iterators.
Devin Jeanpierre
@Devin, I'm not sure which part of my answer you find inaccurate. I never said a `Level` itself would be an iterator, and I think the OP already knew the difference. As I said, the given code is used to implement an iterator. An actual iterator instance is returned when you call `__iter__`
Matthew Flaschen
I never said it wasn't accurate, just that it was confusing. I'm not sure that the OP really did know the difference, since he apparently knows nearly nothing about iterators in Python. The subtle terminology distinctions go with that. A confusing post could be harmful to the overall understanding of iterators and iterables.
Devin Jeanpierre
+3  A: 

If you define a member method called __iter__(self), Python will know how to iterate through it.

In this method, I suggest you use yield to return your data (this is called a generator). You could also return a list or tuple, but this is more efficient memory-wise. Here is an example:

class Test(object):
    def __iter__(self):
        for x in range(10): 
            yield x

l = Test()
for room in l:
    print room
orangeoctopus
+1  A: 

As an alternative to writing a generator, the __iter__ method just needs to return an iterator - if your Level object has an internal datastructure that holds the rooms then you could return it's iterator directly:

class Level(object):
    def __init__(self):
        self.rooms = []

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

If the rooms container is a dictionary e.g. mapping room names to Room objects then you can get a iterator to the Room objects with the dict.itervalues method:

class Level(object):
    def __init__(self):
        self.rooms = {}

    def __iter__(self):
        return self.rooms.itervalues()
Dave Kirby
A: 

Why not use generator.

>>> import timeit
>>> class Test:
    def __iter__(self):
        for i in range(10):
            yield i

>>> t1 = lambda: [k for k in Test()]
>>> timeit.timeit(t1)
3.529460948082189


>>> def test2():
    for i in range(10):
        yield i

>>> t2 = lambda: [k for k in test2()]
>>> timeit.timeit(t2)
3.171831107365392
Selinap