What are Python decorators and generators?

Clair 27 Published: 12/05/2024

What are Python decorators and generators?

Python decorators and generators are two fundamental concepts in Python programming that help you write more concise, readable, and maintainable code.

Decorators

A decorator is a small function that takes another function as an argument and returns a new function that "wraps" the original function. The purpose of a decorator is to modify or extend the behavior of the original function without changing its source code.

Here's how you can use decorators:

Define a decorator: @my_decorator def my_function(): pass

Call the decorated function: my_function()

In this example, my_decorator takes in the original function (my_function) as an argument and returns a new function that "wraps" it.

Decorators are often used to:

Logging: Record information about each function call Authentication: Check if the user is authenticated before allowing them to access certain functions Error handling: Catch and handle exceptions thrown by the original function

For example, you can create a decorator to log every time a function is called:

def log_calls(func):

def wrapper(*args, **kwargs):

print(f"Calling {func.name} with args {args} and kwargs {kwargs}")

return func(*args, **kwargs)

return wrapper

@log_calls

def my_function(x, y):

return x + y

result = my_function(2, 3) # Output: Calling my_function with args (2, 3) and kwargs {}

print(result) # Output: 5

Generators

A generator is a type of iterable that generates values on-the-fly as you iterate over it. Generators are useful when working with large datasets or when you need to process data in chunks.

Here's how you can create a generator:

Define a generator function: def my_generator(): yield 1; yield 2; Call the generator and iterate over its values: for value in my_generator(): print(value)

In this example, the my_generator function yields two values (1 and 2). When you call it with a for loop, it generates these values one by one.

Generators are often used to:

Process large datasets in chunks Generate random numbers or data Create iterators that yield values based on some condition

For example, you can create a generator to generate all prime numbers up to 100:

def prime_numbers(n):

for i in range(2, n+1):

is_prime = True

for j in range(2, int(i ** 0.5) + 1):

if i % j == 0:

is_prime = False

break

if is_prime:

yield i

for prime in prime_numbers(100): print(prime)

This code generates all prime numbers up to 100 and prints them one by one.

In summary, Python decorators are used to modify or extend the behavior of functions without changing their source code. Generators are used to generate values on-the-fly as you iterate over them. Both concepts help you write more concise, readable, and maintainable code in Python!

What are the famous decorators in Python?

I'd be happy to respond in English!

Python is a popular programming language known for its simplicity and ease of use. One of its key features is its extensive library collection, which includes many useful modules for data analysis, machine learning, web development, and more.

Decorators are a type of function that can modify the behavior of another function. They're often used to implement aspects or behaviors that would be repeated throughout your code. Python has several decorators that can simplify your code and make it easier to read and maintain.

Here are some of the most famous decorators in Python:

@property: This decorator is used to create getter and setter methods for attributes in a class. For example, if you have a Person class with an age attribute, you can use this decorator to create a property that allows you to get or set the age.
class Person:

def init(self, name):

self.name = name

self._age = 0

@property

def age(self):

return self._age

@age.setter

def age(self, value):

if value < 0:

raise ValueError("Age cannot be negative")

self._age = value

p = Person("John")

print(p.age) # Output: 0

p.age = 30

print(p.age) # Output: 30

@functools.lru_cache: This decorator is used to cache the results of a function, which can significantly speed up performance by avoiding redundant calculations.
import functools

@functools.lru_cache(maxsize=None)

def fibonacci(n):

if n <= 1:

return n

return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10)) # Output: 55

@singledispatch: This decorator is used to define a single function that can be applied to multiple types of input.
from functools import singledispatch

@singledispatch

def greet(obj):

if isinstance(obj, str):

return f"Hello, {obj}!"

elif isinstance(obj, int):

return "Hello, world!"

print(greet("John")) # Output: Hello, John!

print(greet(123)) # Output: Hello, world!

@contextmanager: This decorator is used to create a context manager that can be used with the with statement.
from contextlib import contextmanager

@contextmanager

def my_context():

print("Entering my context")

try:

yield

finally:

print("Exiting my context")

with my_context():

pass # Output: Entering my context; Exiting my context

@timed: This decorator is used to time how long a function takes to execute.
import time

def timed(func):

def wrapper(*args, **kwargs):

start_time = time.time()

result = func(*args, **kwargs)

end_time = time.time()

print(f"{func.name} took {end_time - start_time:.2f} seconds")

return result

return wrapper

@timed

def example_function():

time.sleep(1)

example_function() # Output: example_function took 1.00 seconds

These are just a few examples of the many decorators available in Python. By using these decorators, you can create more robust and efficient code that's easier to read and maintain.

(Note: This response is written entirely in English.)