backend / python core / 08_class_iterator_generator.md

Class, Iterator, and Generator in Python

3 interview angles 2 min read source

Class, Iterator, and Generator in Python

1. Class

  • Definition: A class is a blueprint for creating objects. It defines attributes (data) and methods (functions) that operate on the data.
  • Purpose: To encapsulate data and functionality together.
  • Example:
    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def greet(self):
            return f"Hello, my name is {self.name} and I am {self.age} years old."
    
    person = Person("Alice", 30)
    print(person.greet())

2. Iterator

  • Definition: An object that implements the __iter__() and __next__() methods to allow sequential access to its elements.
  • Purpose: To iterate over a collection (like lists, tuples, etc.) without exposing its underlying structure.
  • Example:
    class Counter:
        def __init__(self, start, end):
            self.current = start
            self.end = end
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.current > self.end:
                raise StopIteration
            else:
                self.current += 1
                return self.current - 1
    
    counter = Counter(1, 5)
    for num in counter:
        print(num)

3. Generator

  • Definition: A special type of iterator defined using a function with the yield keyword. Generators are used to produce items one at a time as they are needed.
  • Purpose: To save memory by yielding values lazily instead of generating all values at once.
  • Example:
    def fibonacci(n):
        a, b = 0, 1
        for _ in range(n):
            yield a
            a, b = b, a + b
    
    for num in fibonacci(10):
        print(num)

print("\nClass\n")
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

person = Person("Alice", 30)
print(person.greet())
print("\nIterator\n")
class Counter:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current > self.end:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

counter = Counter(1, 5)
for num in counter:
    print(num)
print("\nGenerator\n")
def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

for num in fibonacci(10):
    print(num)

Interview angle

  • “Iterator protocol?” - __iter__ returns an iterator, __next__ returns the next item or raises StopIteration. An iterable only needs __iter__; an iterator needs both and returns itself from __iter__.
  • “Why write a generator instead of an iterator class?” - far less code and the state is implicit in the function’s suspension point. Write the class only when you need extra methods or attributes on the iterator itself.
  • “What does laziness buy you?” - constant memory over arbitrarily large sequences, and the ability to model infinite streams. It’s why you read a large file line by line rather than calling .readlines().