Constructor, Iterator, Generator, and Decorator
Introduction
In this chapter, you will learn four powerful Python concepts: constructor, iterator, generator, and decorator. These tools help you write cleaner object-oriented code, process data efficiently, and extend function behavior without changing core logic. Once you understand them, your Python skill set becomes much more advanced and practical.
Prerequisites
- Python
3.10+installed - Basic understanding of classes, functions, loops, and exceptions
- Ability to run
.pyfiles in terminal or IDE
1) Constructor (__init__)
A constructor is a special method used to initialize object attributes when creating a class instance.
# Define class with constructor
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def show(self):
print(f"{self.name}: {self.score}")
# Create object
stu = Student("Emma", 95)
stu.show()Why constructor matters:
- ensures object has required initial state
- reduces repetitive setup code
2) Iterator
An iterator is an object that returns one item at a time, usually via __iter__() and __next__().
Python built-ins like lists are iterable, and you can create custom iterators too.
# Custom iterator class
class Counter:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
self.current += 1
return self.current
raise StopIteration
# Use custom iterator
for num in Counter(5):
print(num)Iterator gives controlled step-by-step data access.
3) Generator
A generator is a simpler way to create iterators using yield.
# Generator function
def count_up_to(limit):
num = 1
while num <= limit:
yield num
num += 1
# Use generator
for num in count_up_to(5):
print(num)Difference from normal function:
returnends function onceyieldpauses and resumes function state
Tip
Why Generators Are Great
Generators are memory-friendly for large data streams because they produce values lazily.
4) Decorator
A decorator wraps a function to add extra behavior before/after execution.
# Define decorator
def log_call(func):
def wrapper(*args, **kwargs):
print(f"Calling function: {func.__name__}")
result = func(*args, **kwargs)
print("Function finished.")
return result
return wrapper
# Apply decorator
@log_call
def say_hello(name):
print(f"Hello, {name}!")
# Call function
say_hello("Liam")Decorator use cases:
- logging
- timing
- permission checks
- retry wrappers
5) Real Mini Example: Student Score Pipeline
This example combines all four concepts in a lightweight way.
# Decorator: log function calls
def log_call(func):
def wrapper(*args, **kwargs):
print(f"[LOG] Start: {func.__name__}")
result = func(*args, **kwargs)
print(f"[LOG] End: {func.__name__}")
return result
return wrapper
# Class with constructor
class Student:
def __init__(self, name, scores):
self.name = name
self.scores = scores
# Generator: produce score values one by one
def score_generator(scores):
for score in scores:
yield score
# Iterator class: iterate over student names
class StudentNameIterator:
def __init__(self, students):
self.students = students
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.students):
name = self.students[self.index].name
self.index += 1
return name
raise StopIteration
# Decorated function
@log_call
def print_student_report(student):
total = sum(score_generator(student.scores))
avg = total / len(student.scores)
print(f"{student.name} average score: {avg:.2f}")
# Demo data
students = [
Student("Emma", [95, 88, 92]),
Student("Noah", [78, 85, 80]),
]
# Use iterator for names
print("Student names:")
for name in StudentNameIterator(students):
print(name)
# Use decorated report function
for student in students:
print_student_report(student)This shows how these concepts can work together in real code structure.
Warning
Do not use advanced patterns just for style.
Use them when they solve real readability, reuse, or performance problems.
Common Beginner Mistakes
Mistake 1: Confusing Iterable and Iterator
Iterable can be looped over; iterator is the stateful object that produces next values.
Mistake 2: Using return Instead of yield in Generator
return ends function immediately, so it will not behave like a generator stream.
Mistake 3: Forgetting *args and **kwargs in Decorator Wrapper
Without them, decorated functions with parameters can break.
Surprise Practice Challenge
Build a "Reading Task Tracker" project:
- Class
Taskwith constructor (title,done) - Generator that yields unfinished task titles
- Custom iterator for all task objects
- Decorator that logs each task-update function call
- Print final summary report
If you finish this, you can confidently use intermediate Python design patterns.
FAQ
Should beginners use custom iterators often?
Not often in simple scripts. Start with generators first, then custom iterators when needed.
Are generators always better than lists?
Not always. Generators are better for streaming/lazy processing; lists are better when you need random access or repeated traversal.
Do decorators change the original function code?
They do not require editing internal logic, but they wrap function behavior externally.
When should I avoid decorators?
Avoid decorators when the team is unfamiliar with them and the added abstraction hurts readability.