1.1 Decorators and Closures
Decorators provide a simple syntax for calling higher-order functions. A decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_whee():
print("Whee!")
1.2 Generators and Iterators
Generators are a simple way of creating iterators. They allow you to declare a function that behaves like an iterator, i.e. it can be used in a for loop.
- yield keyword: Pauses the function saving all its states and later continues from there on successive calls.
- Memory Efficiency: Generators are memory efficient as they yield items one at a time.
1.3 Context Managers
Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement.
1.4 Metaclasses
Metaclasses are the 'classes of classes'. They allow you to intercept the creation of classes and modify them. This is an advanced feature often used in frameworks (like Django ORM).
🎯 Practical Exercise
Create a custom decorator @timer that measures the execution time of any function it decorates and logs it to the console.