12

I'm confused by Python's for loop syntax. Consider a code example like:

for party in feed.entry:
    print(party.location.address.text)

What does for party in feed.entry actually mean, given that feed.entry is an iterable? Step by step, what happens when the loop runs?


This question is primarily about how the loop syntax is implemented, on a technical level. For a beginner-level explanation of the syntax, see Understanding for loops in Python.

To close debugging questions with the common problem wherein OP has incorrectly attempted y[x] within a loop like for x in y:, use Why do I get an IndexError (or TypeError, or just wrong results) from "ar[i]" inside "for i in ar"?.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • I think you need a simple tutorial: http://www.dreamsyssoft.com/python-scripting-tutorial/loops-tutorial.php – Rocky Pulley Jan 09 '13 at 15:35
  • The language feature that's new to you is an [**iterable** is an object capable of returning its members one at a time](https://docs.python.org/3/glossary.html#term-iterable). See also https://stackoverflow.com/a/9884259/202229 . Where Python differs from C++ is you don't need to know exactly what type of object `feed.entry` or set up all the clunky C++ scaffolding ([Drakosha's answer](https://stackoverflow.com/a/1292205/202229)) before using it. You simply say `for x in ` – smci Sep 05 '19 at 12:28

7 Answers7

25

feed.entry is property of feed and it's value is (if it's not, this code will fail) object implementing iteration protocol (array, for example) and has iter method, which returns iterator object

Iterator has next() method, returning next element or raising exception, so python for loop is actually:

iterator = feed.entry.__iter__()
while True:
    try:
        party = iterator.next()
    except StopIteration:
        # StopIteration exception is raised after last element
        break

    # loop code
    print party.location.address.text
ymv
  • 2,123
  • 13
  • 21
  • 13
    +1, as giving equivalent lower-level code is often the best explanation; wish I could give only +0.9 since the first line should be `iter(feed.entry)` (most of the time when you're calling a special method directly rather than through a builtin you're doing it wrong, though there are exceptions to this;-). – Alex Martelli Aug 18 '09 at 15:16
  • 1
    Note that in Python 3 the magic `next` method is now called `__next__`. And you can invoke it using the [`next`](https://docs.python.org/3/library/functions.html#next) built-in function. – PM 2Ring Nov 20 '19 at 18:49
6

feed.entry is something that allows iteration, and contains objects of some type. This is roughly similar to c++:

for (feed::iterator party = feed.entry.begin(); party != feed.entry.end(); ++party) {
   cout << (*party).location.address.text;
}
Drakosha
  • 11,925
  • 4
  • 39
  • 52
  • If i remember correctly, it's how old (pre 2.0) python worked (using length(), [] and counter) – ymv Aug 18 '09 at 06:56
3

Formally a for statement in Python always operates on an iterable -- an object which can provide an iterator over its items. The for statement successively fetches the next element from the iterator, assigns it to the target name(s) and runs the suite ("body") with that.

#   |name|   |iterable|
for party in feed.entry:
    # body...
    print(party.location.address.text)

In the example, feed.entry is the iterable, party is the target name and print ... is the suite. The iterator is automatically requested by the for statement, and holds the iteration state - e.g. the index of the next element if the iterable is a list.


If you are coming from C++, a classical for (int i = 0; i < 10; ++i) loop represents external iteration: the iteration state i is kept outside of the iterable. This corresponds to Python's while loop:

# for (int i = 0; i < 10; ++i)
i = 0
while i < 10:
    i += 1
    # access the state of an iterable here

The newer for (auto party : entry) range loop represents internal iteration: the iteration state is kept by a separate iterator. This corresponds to Python's for loop. However, the iterable/iterator protocol differs notably: Python's for uses iter(iterable) to get an iterator, which should support next(iterator) - either returning an element or raising StopIteration.

Written in Python, the definition of the for statement corresponds to this:

# for party in feed.entry:
__iterator = iter(feed.entry)  # iterator -- not visible in containing scope
__iterating = True             # graceful exit to enter `else` clause
while __iterating:
    try:                          # attempt to...
        item = next(__iterator)   # ... get the next item
    except StopIteration:         # ... or stop
        __iterating = False       #     with a graceful exit
    else:
        party = item
        <suite>                # run the body with names bound
else:                          # entered in a graceful exit only
    <else suite>

(Note that the entire block from __iterating = True to __iterating = False is not "visible" to the containing scope. Implementations use various optimisations, such as CPython allowing builtin iterators to return a C NULL instead of raising a Python StopIteration.)

The for statement just defines how iterable and iterator are used. If you are mostly familiar with external iteration, it helps looking at iterable and iterator as well.


The iter(iterable) call has multiple ways to derive an iterator - this is as if iter were overloaded for various structural types.

  • If type(iterable).__iter__ is defined, it is called as a method and the result is used as the iterator.

  • If type(iterable).__getitem__ is defined, it is wrapped by a generic iterator type that returns iterable[0], iterable[1], ... and raises StopIteration if IndexError is raised when indexing.

Either way, iter returns an iterator or raises TypeError. An iterator is any type that defines __iter__ (for reusability) and __next__ (for the actual iteration). In general, iterators are objects that may hold state to compute the __next__ item. For example, a list iterator corresponds to this object:

class ListIterator:
    """Python equivalent of ``iter(:list)``"""
    # iterator holds iteration state - e.g. iterable and current index
    def __init__(self, iterable: list):
        self.iterable = iterable
        self.index = 0

    # __next__ fetches item and advances iteration state - e.g. index += 1
    def __next__(self):
        # attempt to produce an item
        try:
            item = self.iterable[self.index]
        except IndexError:  # translate indexing protocol to iteration protocol
            raise StopIteration
        # update iteration state
        self.index += 1
        return item

    # iterators can be iterated on in ``for`` statements etc.
    def __iter__(self):
        return self

(Note that one would idiomatically write such an object as a generator function.)

Indexing a list or incrementing some pointer is only a very basic example of the iterable/iterator protocol. For example, an iterator could be stateless and use random.random() in __next__ to produce an infinite stream of random numbers. Iterators can also hold state of external information, and iteratively traverse a file system, for example.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
2

party simply iterates over the iterable feed.entry

Take a look at Dive into Python explainations.

Pierre-Jean Coudert
  • 9,109
  • 10
  • 50
  • 59
1

In Python, for bucles aren't like the C/C++ ones, they're most like PHP's foreach. What you do isn't iterate like in a while with "(initialization; condition; increment)", it simply iterates over each element in a list (strings are ITERABLE like lists).

For example:

for number in range(5):
    print number

will output

0
1
2
3
4
ramosg
  • 2,046
  • 2
  • 15
  • 14
  • 2
    strings are NOT lists. Strings are iterables, and so are lists, but strings and lists are certainly two distinct types in Python. – Kenan Banks Aug 18 '09 at 11:23
  • Well, in many languages strings are arrays of characters, and in Python its behavior is very similar when iterating... edit... – ramosg Aug 18 '09 at 12:28
  • 4
    Yes, strings and lists are very similar *when iterating* but importantly different in many other cases. For instance, you cannot modify a string. – Kenan Banks Aug 18 '09 at 14:09
1

I just wanted to provide a more generic explanation for people new to this.

Syntactically, the for loop looks like...

for <name to be assigned to> in <some sequence>:

    <block of code, optionally referencing the name>

The interpreter runs the block once for each item in the sequence (anything that can iterated over). Each time it runs the block, it first assigns the next object in the sequence to the name, which can be any valid variable name.

Doing for each in (1, 2, 3): print(each) is [more or less] the same as doing...

i = 0
sequence = (1, 2, 3)
while True:
    try:
        each = sequence[i]
        print(each)
        i += 1
    except IndexError: pass

You can also unpack arguments in the assignment part. In the same way that you can do stuff like...

a, b = 1, 2

...you can also do stuff like...

for a, b in [(1, 2), (3, 4), (5, 6)]: print(a + b)

...which prints...

3
7
11
Carl Smith
  • 3,025
  • 24
  • 36
-1

Python's for loop works with iterators, which must implement the iterator protocol. For more details see:

Community
  • 1
  • 1
ars
  • 120,335
  • 23
  • 147
  • 134