Read And Learn
Python
Chapter 5: Iterators, Generators & Decorators
An In-Depth Comprehensive Guide for Beginners
1. Iterators: Under the Hood of Loops
In Python, an iterator is an object that contains a countable number of values.
Iterators are the hidden engine behind for loops. When you loop over a list, tuple,
or string, Python automatically converts it into an iterator.
Technically, an iterator is any object that implements the iterator protocol, which
consists of two methods: __iter__() and __next__() .
my_list = ["apple", "banana", "cherry"]
my_iter = iter(my_list) # Get the iterator object
print(next(my_iter)) # Outputs: apple
print(next(my_iter)) # Outputs: banana
print(next(my_iter)) # Outputs: cherry
# Calling next() again would raise a StopIteration exception
Understanding iterators is essential because it allows you to build custom objects
that can be looped over, yielding elements one at a time rather than storing
everything in memory at once.
2. Generators: Lazy Evaluation
Generators are a simple way of creating iterators. A generator is a function that
returns an object (iterator) which we can iterate over (one value at a time). Instead
of using the return statement, a generator uses the yield keyword.
When a generator function is called, it does not execute the function body
immediately. Instead, it returns a generator object. When next() is called on this
object, the function executes until it hits the yield statement, pausing its state
(remembering all variable values) until the next call.
def simple_countdown(num):
print("Starting countdown...")
while num > 0:
yield num
num -= 1
# Using the generator
gen = simple_countdown(3)
print(next(gen)) # Prints: Starting countdown... then 3
print(next(gen)) # Prints: 2
print(next(gen)) # Prints: 1
Why use Generators?
Generators are incredibly memory efficient. If you need to process a dataset
with 10 million rows, loading it all into a list would crash your computer's
RAM. A generator loads and processes one row at a time, keeping your
memory footprint minimal.
3. Decorators: Modifying Function
Behavior
A decorator is a design pattern in Python that allows a user to add new functionality
to an existing object without modifying its structure. Decorators are usually called
before the definition of a function you want to decorate, using the @ symbol.
In Python, functions are "first-class citizens," meaning they can be passed as
arguments to other functions, and functions can return functions.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is
called.")
func() # Call the original function
print("Something is happening after the function is
called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
When you run say_hello() , it actually executes the wrapper() function
provided by the decorator. This is extensively used in web frameworks (like Flask
and Django) for routing, authentication, and logging.
4. Decorators with Arguments
What if the function you want to decorate takes arguments? Your wrapper function
needs to accept those arguments using *args and **kwargs .
def logger(func):
def wrapper(*args, **kwargs):
print(f"Executing {func.__name__} with arguments {args} and
{kwargs}")
result = func(*args, **kwargs)
print(f"Finished executing {func.__name__}")
return result
return wrapper
@logger
def add_numbers(a, b):
return a + b
print(add_numbers(5, 10))
5. Practice Exercises
Iterators, Generators, and Decorators are considered intermediate-to-advanced
Python concepts. The following 45 exercises will heavily drill these mechanics into
your muscle memory.
Exercise 1: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 1: Iterator Mechanics
items = ['A_1', 'B_1', 'C_1']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 2: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 2: Basic Yielding
def produce_values():
yield 2
yield 4
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 3: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 3: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 4: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 4: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 5: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 5: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(5, 7))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 6: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 6: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test6'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 7: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 7: Iterator Mechanics
items = ['A_7', 'B_7', 'C_7']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 8: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 8: Basic Yielding
def produce_values():
yield 8
yield 16
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 9: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 9: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 10: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 10: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 11: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 11: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(11, 13))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 12: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 12: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test12'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 13: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 13: Iterator Mechanics
items = ['A_13', 'B_13', 'C_13']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 14: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 14: Basic Yielding
def produce_values():
yield 14
yield 28
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 15: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 15: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 16: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 16: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 17: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 17: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(17, 19))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 18: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 18: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test18'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 19: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 19: Iterator Mechanics
items = ['A_19', 'B_19', 'C_19']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 20: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 20: Basic Yielding
def produce_values():
yield 20
yield 40
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 21: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 21: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 22: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 22: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 23: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 23: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(23, 25))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 24: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 24: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test24'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 25: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 25: Iterator Mechanics
items = ['A_25', 'B_25', 'C_25']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 26: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 26: Basic Yielding
def produce_values():
yield 26
yield 52
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 27: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 27: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 28: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 28: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 29: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 29: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(29, 31))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 30: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 30: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test30'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 31: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 31: Iterator Mechanics
items = ['A_31', 'B_31', 'C_31']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 32: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 32: Basic Yielding
def produce_values():
yield 32
yield 64
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 33: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 33: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 34: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 34: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 35: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 35: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(35, 37))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 36: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 36: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test36'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 37: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 37: Iterator Mechanics
items = ['A_37', 'B_37', 'C_37']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 38: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 38: Basic Yielding
def produce_values():
yield 38
yield 76
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 39: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 39: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Exercise 40: Simple Decorators
Problem Statement: Develop a Python script that applies the concept of
simple decorators effectively.
Python Code Solution:
# Exercise 40: Simple Decorators
def tagger(func):
def wrapper():
return f'' + func() + f''
return wrapper
@tagger
def get_text():
return 'Data'
print(get_text())
Detailed Explanation: This exercise tests your grasp of simple decorators.
We define a basic decorator that wraps the output of another function with
XML/HTML-like tags. The @tagger syntax dynamically replaces get_text with
the inner wrapper function.
Exercise 41: Decorators with Args
Problem Statement: Develop a Python script that applies the concept of
decorators with args effectively.
Python Code Solution:
# Exercise 41: Decorators with Args
def debug(func):
def wrap(*args, **kwargs):
print('Args:', args)
return func(*args, **kwargs)
return wrap
@debug
def multiply(x, y):
return x * y
print(multiply(41, 43))
Detailed Explanation: This exercise tests your grasp of decorators with
args. To support decorated functions that require inputs, the inner wrapper
function must capture all positional (*args) and keyword (**kwargs)
arguments and pass forward to the original function.
Exercise 42: Chained Decorators
Problem Statement: Develop a Python script that applies the concept of
chained decorators effectively.
Python Code Solution:
# Exercise 42: Chained Decorators
def upper_dec(func):
return lambda: func().upper()
def dash_dec(func):
return lambda: '-'.join(func())
@upper_dec
@dash_dec
def word():
return 'test42'
print(word())
Detailed Explanation: This exercise tests your grasp of chained decorators.
Decorators can be stacked. Python applies them from the bottom up (closest
to the function first). Here, word() is first dashed, and then the dashed result
is capitalized by the upper_dec.
Exercise 43: Iterator Mechanics
Problem Statement: Develop a Python script that applies the concept of
iterator mechanics effectively.
Python Code Solution:
# Exercise 43: Iterator Mechanics
items = ['A_43', 'B_43', 'C_43']
my_iter = iter(items)
print(next(my_iter))
print(next(my_iter))
Detailed Explanation: This exercise tests your grasp of iterator mechanics.
We manually extract an iterator from a list using iter(), and then advance it
using next(). This demonstrates how Python inherently manages state
traversal in background loops.
Exercise 44: Basic Yielding
Problem Statement: Develop a Python script that applies the concept of
basic yielding effectively.
Python Code Solution:
# Exercise 44: Basic Yielding
def produce_values():
yield 44
yield 88
for val in produce_values():
print('Yielded:', val)
Detailed Explanation: This exercise tests your grasp of basic yielding. The
function uses the 'yield' keyword instead of 'return'. This turns the function
into a generator. The for-loop automatically handles the StopIteration
exception.
Exercise 45: Stateful Generators
Problem Statement: Develop a Python script that applies the concept of
stateful generators effectively.
Python Code Solution:
# Exercise 45: Stateful Generators
def counter(start):
while start < start + 3:
yield start
start += 1
if start > 50: break
gen = counter(10)
print(list(gen))
Detailed Explanation: This exercise tests your grasp of stateful generators.
Generators inherently remember their state between yields. Here, the 'start'
variable's state is preserved, updated, and evaluated sequentially across
multiple iterations without storing an entire array in memory.
Conclusion
Congratulations! You have completed Chapter 5 of the Read And Learn Python
series. You now understand how loops actually work under the hood using Iterators.
You have learned how to handle massive datasets memory-efficiently using
Generators and the yield keyword. Finally, you've unlocked the power of
metaprogramming by modifying function behavior on the fly using Decorators.
These are advanced concepts that separate beginner programmers from seasoned
Pythonic developers.