An iterator is any object that implements two methods:
Creating a Basic Iterator:
class Counter: def __init__(self, start, end): self.current = start self.end = end def __iter__(self): return self # Returns itself as an iterator def __next__(self): if self.current >= self.end: raise StopIteration self.current += 1 return self.current - 1 counter = Counter(1, 4) for number in counter: print(number) # Outputs: 1, 2, 3
This class manually controls the next() call, stopping when it reaches the end. Iterators are beneficial for working with sequences where each element is processed on-demand.
A generator is a simpler way to create an iterator. Defined with a function that uses the yield keyword, it suspends function execution at yield and resumes it when next() is called. Each yield statement saves the function’s state, meaning it can pick up where it left off.
Basic Generator Example:
def countdown(num): while num > 0: yield num num -= 1 for n in countdown(3): print(n) # Outputs: 3, 2, 1
When yield is called, the function returns the current value and pauses, waiting for next() to resume.
Generators compute values on-the-fly, which is called lazy evaluation. Unlike lists, which store all items in memory, generators produce items only as needed, which is ideal for:
Example: Reading Large Files with Generators:
def read_large_file(file_path): with open(file_path) as file: for line in file: yield line # Only processes one line at a time
This approach prevents loading the entire file into memory, which is particularly useful for massive files.
A generator expression is a succinct way to create generators, using parentheses instead of square brackets like list comprehensions.
Example:
squares = (x * x for x in range(5)) print(next(squares)) # Outputs: 0 print(list(squares)) # Outputs remaining: [1, 4, 9, 16]
Here, squares only computes values when requested, making it memory-efficient.
The yield from statement is useful for delegating part of a generator’s operations to another generator. This is helpful when you want to break a generator into sub-generators for modularity.
Example:
def generator_a(): yield 1 yield 2 def generator_b(): yield from generator_a() yield 3 for val in generator_b(): print(val) # Outputs: 1, 2, 3
yield from streamlines code, especially in complex or nested generator chains.
Generators are particularly useful when:
Lists, on the other hand, are better when:
With iterators and generators, Python gives you control over data processing with memory efficiency and flexibility. They’re essential for handling large datasets, streaming data, and building custom iterable objects.
Master these, and you’ll be handling data like a Python pro! ?
The above is the detailed content of Deep Understanding on Python Iterators: Navigating Data with `__iter__` and `__next__`. For more information, please follow other related articles on the PHP Chinese website!