🚀 Alguni Start learning

Unit 19: Making Your Own Loops

Iterators, generators and promises.

Unit 19 of 31 in Python for kids. Its 4 lessons are How For Loops Really Work, The Easy Way: yield, Promising a Method Exists and Loop Master — below is everything each one explains, and a question or two from it to try.

Every sample on this page was run through real Python before it shipped, and prints exactly what it says it prints.

This unit opens with a fortnight’s trial of everything — no card needed — or with a family plan, bought in the iPhone app. The first two units of every track are free for ever. Try it in the app.

⚙️ How For Loops Really Work

A for loop is asking "what next?"

A for loop does not magically know a list. It keeps asking the thing for its next item, until the thing says there are no more. Anything that can answer those questions can be looped over.

Python

nums = [1, 2, 3]
it = iter(nums)
print(next(it))
print(next(it))
print(next(it))

It prints

1
2
3

Saying "no more"

When there is nothing left, next raises StopIteration — and that is exactly the signal a for loop watches for to know it is finished.

Python

it = iter([1])
print(next(it))
try:
    next(it)
except StopIteration:
    print("that is the end")

It prints

1
that is the end

Building your own

Give a class __iter__ (which hands back the thing doing the counting) and __next__ (which gives the next item), and for can loop over it like anything else.

Python

class CountTo:
    def __init__(self, limit):
        self.limit = limit
        self.n = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.n >= self.limit:
            raise StopIteration
        self.n = self.n + 1
        return self.n

for number in CountTo(3):
    print(number)

It prints

1
2
3

Try it yourself

What tells a for loop to stop?

  • Running out of memory
  • The iterator raising StopIteration
  • Returning None
  • The loop counts the items first

What does this print?

Python

it = iter("ab")
print(next(it))
print(next(it))

Answer them in the app

🌱 The Easy Way: yield

All of that, in three lines

A function with yield in it is a generator. It hands out one value and pauses right there, carrying on from the same spot when asked for the next.

Python

def count_to(limit):
    n = 1
    while n <= limit:
        yield n
        n = n + 1

for number in count_to(3):
    print(number)

It prints

1
2
3

return ends, yield pauses

A return finishes a function for good. A yield steps out for a moment and remembers exactly where it was.

Python

def three():
    print("starting")
    yield 1
    print("woke up again")
    yield 2

for n in three():
    print(n)

It prints

starting
1
woke up again
2

It only makes what you ask for

This is the real reason generators exist. A list of a million numbers has to exist all at once; a generator makes each one only when it is wanted, so it costs almost no memory.

Python

def forever():
    n = 1
    while True:
        yield n
        n = n + 1

for n in forever():
    if n > 3:
        break
    print(n)

It prints

1
2
3

Try it yourself

What makes a function a generator?

  • A special decorator
  • Having the word yield somewhere inside it
  • Returning a list
  • Inheriting from Generator

What does this print?

Python

def evens(limit):
    for n in range(limit):
        if n % 2 == 0:
            yield n

print(list(evens(7)))

Answer them in the app

🤞 Promising a Method Exists

A class that refuses to be built

An abstract class is a plan, not a thing. It says "anyone inheriting me must have this method" — and Python will not let you build the plan itself.

Python

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

try:
    Shape()
except TypeError:
    print("cannot build a plan, only a real shape")

It prints

cannot build a plan, only a real shape

Children must keep the promise

A child that provides the method works fine. One that forgets is refused — and you find out straight away, instead of much later when something calls the missing method.

Python

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side * self.side

print(Square(4).area())

It prints

16

This is abstraction

Once every shape promises an area(), whoever uses them never has to know or care which kind they have. That is abstraction — hiding the details behind a shared promise.

Python

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Square(Shape):
    def __init__(self, s):
        self.s = s
    def area(self):
        return self.s * self.s

class Rect(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h
    def area(self):
        return self.w * self.h

for shape in [Square(2), Rect(2, 5)]:
    print(shape.area())

It prints

4
10

Try it yourself

What is the point of an abstract method?

  • It runs before the others
  • It forces every child class to provide that method
  • It makes the class faster
  • It hides the method

Answer it in the app

🏆 Loop Master

Try it yourself

What does this print?

Python

def gen():
    yield "a"
    yield "b"

it = gen()
print(next(it))
print(next(it))

Answer it in the app