views:

245

answers:

2

I have the following code in django.template:

class Template(object):
    def __init__(self, template_string, origin=None, name='<Unknown Template>'):
        try:
            template_string = smart_unicode(template_string)
        except UnicodeDecodeError:
            raise TemplateEncodingError("Templates can only be constructed from unicode or UTF-8 strings.")
        if settings.TEMPLATE_DEBUG and origin is None:
            origin = StringOrigin(template_string)
        self.nodelist = compile_string(template_string, origin)
        self.name = name

    def __iter__(self):
        for node in self.nodelist:
            for subnode in node:
                yield subnode

    def render(self, context):
        "Display stage -- can be called many times"
        return self.nodelist.render(context)

The part I am confused about is below. How does this __iter__ method work? I can't find any corresponding next method.

def __iter__(self):
        for node in self.nodelist:
            for subnode in node:
                yield subnode

This is the only way that I know how to implement __iter__:

class a(object):
    def __init__(self,x=10):
        self.x = x
    def __iter__(self):
        return self
    def next(self):
        if self.x > 0:
            self.x-=1
            return self.x
        else:
            raise StopIteration
 ainst = a()
 for item in aisnt:
     print item

In your answers, please try to use code examples rather than text, because my English is not very good. Thank you.

+4  A: 

That __iter__method returns a python generator (see the documentation), as it uses the yield keyword. The generator will provide the next() method automatically; quoting the documentation:

What makes generators so compact is that the __iter__() and next() methods are created automatically.

EDIT:

Generators are really useful. If you are not familiar with them, I suggest you readup on them, and play around with some test code.

Here is some more info on iterators and generators from StackOverflow.

catchmeifyoutry
Is it meant to be called by client code? I assumed not since it has the __fname__ form
danben
the --fname-- form where those dashes are underscores
danben
I have no idea about Django, but in the end it's just python syntax. When a iterator needs to be created for a Template object, its `__iter__(self)` hook will be called. It will then return a Generator object which has the correct next() method.
catchmeifyoutry
Right - so is there any other way that you're supposed to call it besides using for/in?
danben
"it" in this case being `__iter__`, as I am currently under the impression that any function beginning with `__` is not meant to be called
danben
@danben: You don't call `obj.__iter__()`, you call `iter(obj)`. `iter(obj)` will translate into a call to `obj.__iter__()` if it is defined, much like `Foo()` will translate into a call to `Foo.__init__()` if it is defined.
Daniel Pryden
methods/member variables of the form `__x__` usually have a special meaning in python (e.g. `__dict__` and `__builtin__`). The `__iter__` method of an object is called when trying to iterate over the object, such as in a `for .. in ..:` loop.
catchmeifyoutry
+6  A: 

From the docs:

If a container object’s __iter__() method is implemented as a generator, it will automatically return an iterator object (technically, a generator object) supplying the __iter__() and next() methods.

Mark Peters
nice and short, but i still prefer my first answer ;) unless you explain _why_ it is generator in this case.
catchmeifyoutry