Python - Decorators

Python - Decorators

Decorators in Python

Python decorators are a powerful and expressive tool that allows programmers to modify the behavior of a function or class method without modifying its actual source code. Decorators are heavily used in frameworks such as Flask and Django, and also in standard libraries such as unittest and functools.

What is a Decorator?

A decorator in Python is a function that takes another function as input and extends or alters its behavior. Decorators make code more modular, readable, and DRY (Don’t Repeat Yourself).

Basic Function Example

def greet():
    return "Hello!"

print(greet())

Now suppose we want to add some logging functionality whenever `greet` is called. Instead of modifying the function directly, we can use a decorator.

Creating a Simple Decorator

def log_decorator(func):
    def wrapper():
        print("Function is about to run")
        result = func()
        print("Function has run")
        return result
    return wrapper

@log_decorator
def greet():
    return "Hello!"

print(greet())

The output will be:

Function is about to run
Function has run
Hello!

Understanding the @ Syntax

The `@decorator_name` syntax is just syntactic sugar for:

greet = log_decorator(greet)

Decorators with Arguments

Sometimes, the function you want to decorate takes arguments. You need to make sure the wrapper can accept arbitrary arguments.

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def add(x, y):
    return x + y

print(add(10, 5))

Returning Values

It’s important that the wrapper returns the result of the original function to maintain expected functionality.

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print("Logging before function call")
        result = func(*args, **kwargs)
        print("Logging after function call")
        return result
    return wrapper

Multiple Decorators

You can stack multiple decorators on a single function. The decorators are applied from the inside out (bottom to top).

def decorator_one(func):
    def wrapper(*args, **kwargs):
        print("Decorator One")
        return func(*args, **kwargs)
    return wrapper

def decorator_two(func):
    def wrapper(*args, **kwargs):
        print("Decorator Two")
        return func(*args, **kwargs)
    return wrapper

@decorator_one
@decorator_two
def say_hello():
    print("Hello!")

say_hello()

Decorators with Parameters

If you want your decorator itself to accept arguments, you need to add another layer of function nesting.

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def greet():
    print("Hi!")

greet()

Using functools.wraps

When wrapping functions with decorators, Python loses metadata like function name and docstring. Use `functools.wraps` to preserve it.

from functools import wraps

def log_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Calling function:", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def greet():
    """Greet function docstring"""
    print("Hello!")

print(greet.__name__)
print(greet.__doc__)

Class Decorators

You can also use decorators on classes. Here's a simple example:

def decorator(cls):
    cls.is_decorated = True
    return cls

@decorator
class MyClass:
    pass

print(MyClass.is_decorated)

Decorators in Real Life

Timing Function Execution

import time

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"Execution time: {end - start:.4f} seconds")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)
    return "Done"

slow_function()

Authentication Example

def require_admin(func):
    def wrapper(user_role, *args, **kwargs):
        if user_role != "admin":
            return "Access Denied"
        return func(user_role, *args, **kwargs)
    return wrapper

@require_admin
def view_dashboard(user_role):
    return "Dashboard content"

print(view_dashboard("admin"))
print(view_dashboard("user"))

Using Built-in Decorators

@staticmethod and @classmethod

class MyClass:

    @staticmethod
    def static_method():
        print("I am a static method")

    @classmethod
    def class_method(cls):
        print("I am a class method")

MyClass.static_method()
MyClass.class_method()

@property

class Temperature:

    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32

temp = Temperature(25)
print(temp.fahrenheit)

Chaining Decorators with Parameters

Sometimes, you want to apply multiple parameterized decorators. The syntax is the same:

def outer_decorator(param):
    def actual_decorator(func):
        def wrapper(*args, **kwargs):
            print(f"Decorator parameter: {param}")
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

@outer_decorator("Value1")
@outer_decorator("Value2")
def my_func():
    print("Executing function")

my_func()

Common Pitfalls

Not returning wrapper

def bad_decorator(func):
    print("Decorating")
    # Forgot to return a function!
    
@bad_decorator
def test():
    print("Test function")

This will raise an error because the decorator doesn't return a callable.

Overwriting Original Function

def bad_decorator(func):
    def wrapper():
        print("Something")
    return wrapper

@bad_decorator
def add(a, b):
    return a + b

print(add(1, 2))  # This will print None

Decorators in Flask

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Welcome to the homepage"

@app.route('/about')
def about():
    return "About Page"

Decorators for Caching

from functools import lru_cache

@lru_cache(maxsize=32)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

print(fib(30))

Debugging Decorators

If you're using multiple decorators, especially parameterized ones, debugging can be tricky. Always use `functools.wraps` and avoid deeply nested logic unless necessary.

Python decorators provide a concise, readable, and reusable way to alter or enhance functions and methods. They are foundational to Python's design, used extensively in web development, data science, and system utilities. Once you master decorators, you’ll find yourself writing more elegant, modular, and DRY code. From logging, authentication, caching, to even more complex metaprogramming, decorators are a Swiss Army knife in the Python developer's toolkit.

Beginner 5 Hours
Python - Decorators

Decorators in Python

Python decorators are a powerful and expressive tool that allows programmers to modify the behavior of a function or class method without modifying its actual source code. Decorators are heavily used in frameworks such as Flask and Django, and also in standard libraries such as unittest and functools.

What is a Decorator?

A decorator in Python is a function that takes another function as input and extends or alters its behavior. Decorators make code more modular, readable, and DRY (Don’t Repeat Yourself).

Basic Function Example

def greet(): return "Hello!" print(greet())

Now suppose we want to add some logging functionality whenever `greet` is called. Instead of modifying the function directly, we can use a decorator.

Creating a Simple Decorator

def log_decorator(func): def wrapper(): print("Function is about to run") result = func() print("Function has run") return result return wrapper @log_decorator def greet(): return "Hello!" print(greet())

The output will be:

Function is about to run Function has run Hello!

Understanding the @ Syntax

The `@decorator_name` syntax is just syntactic sugar for:

greet = log_decorator(greet)

Decorators with Arguments

Sometimes, the function you want to decorate takes arguments. You need to make sure the wrapper can accept arbitrary arguments.

def log_decorator(func): def wrapper(*args, **kwargs): print(f"Calling function {func.__name__}") return func(*args, **kwargs) return wrapper @log_decorator def add(x, y): return x + y print(add(10, 5))

Returning Values

It’s important that the wrapper returns the result of the original function to maintain expected functionality.

def log_decorator(func): def wrapper(*args, **kwargs): print("Logging before function call") result = func(*args, **kwargs) print("Logging after function call") return result return wrapper

Multiple Decorators

You can stack multiple decorators on a single function. The decorators are applied from the inside out (bottom to top).

def decorator_one(func): def wrapper(*args, **kwargs): print("Decorator One") return func(*args, **kwargs) return wrapper def decorator_two(func): def wrapper(*args, **kwargs): print("Decorator Two") return func(*args, **kwargs) return wrapper @decorator_one @decorator_two def say_hello(): print("Hello!") say_hello()

Decorators with Parameters

If you want your decorator itself to accept arguments, you need to add another layer of function nesting.

def repeat(n): def decorator(func): def wrapper(*args, **kwargs): for _ in range(n): func(*args, **kwargs) return wrapper return decorator @repeat(3) def greet(): print("Hi!") greet()

Using functools.wraps

When wrapping functions with decorators, Python loses metadata like function name and docstring. Use `functools.wraps` to preserve it.

from functools import wraps def log_decorator(func): @wraps(func) def wrapper(*args, **kwargs): print("Calling function:", func.__name__) return func(*args, **kwargs) return wrapper @log_decorator def greet(): """Greet function docstring""" print("Hello!") print(greet.__name__) print(greet.__doc__)

Class Decorators

You can also use decorators on classes. Here's a simple example:

def decorator(cls): cls.is_decorated = True return cls @decorator class MyClass: pass print(MyClass.is_decorated)

Decorators in Real Life

Timing Function Execution

import time def timer(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"Execution time: {end - start:.4f} seconds") return result return wrapper @timer def slow_function(): time.sleep(2) return "Done" slow_function()

Authentication Example

def require_admin(func): def wrapper(user_role, *args, **kwargs): if user_role != "admin": return "Access Denied" return func(user_role, *args, **kwargs) return wrapper @require_admin def view_dashboard(user_role): return "Dashboard content" print(view_dashboard("admin")) print(view_dashboard("user"))

Using Built-in Decorators

@staticmethod and @classmethod

class MyClass: @staticmethod def static_method(): print("I am a static method") @classmethod def class_method(cls): print("I am a class method") MyClass.static_method() MyClass.class_method()

@property

class Temperature: def __init__(self, celsius): self._celsius = celsius @property def fahrenheit(self): return self._celsius * 9/5 + 32 temp = Temperature(25) print(temp.fahrenheit)

Chaining Decorators with Parameters

Sometimes, you want to apply multiple parameterized decorators. The syntax is the same:

def outer_decorator(param): def actual_decorator(func): def wrapper(*args, **kwargs): print(f"Decorator parameter: {param}") return func(*args, **kwargs) return wrapper return actual_decorator @outer_decorator("Value1") @outer_decorator("Value2") def my_func(): print("Executing function") my_func()

Common Pitfalls

Not returning wrapper

def bad_decorator(func): print("Decorating") # Forgot to return a function! @bad_decorator def test(): print("Test function")

This will raise an error because the decorator doesn't return a callable.

Overwriting Original Function

def bad_decorator(func): def wrapper(): print("Something") return wrapper @bad_decorator def add(a, b): return a + b print(add(1, 2)) # This will print None

Decorators in Flask

from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Welcome to the homepage" @app.route('/about') def about(): return "About Page"

Decorators for Caching

from functools import lru_cache @lru_cache(maxsize=32) def fib(n): if n < 2: return n return fib(n-1) + fib(n-2) print(fib(30))

Debugging Decorators

If you're using multiple decorators, especially parameterized ones, debugging can be tricky. Always use `functools.wraps` and avoid deeply nested logic unless necessary.

Python decorators provide a concise, readable, and reusable way to alter or enhance functions and methods. They are foundational to Python's design, used extensively in web development, data science, and system utilities. Once you master decorators, you’ll find yourself writing more elegant, modular, and DRY code. From logging, authentication, caching, to even more complex metaprogramming, decorators are a Swiss Army knife in the Python developer's toolkit.

Frequently Asked Questions for Python

Python is commonly used for developing websites and software, task automation, data analysis, and data visualisation. Since it's relatively easy to learn, Python has been adopted by many non-programmers, such as accountants and scientists, for a variety of everyday tasks, like organising finances.


Python's syntax is a lot closer to English and so it is easier to read and write, making it the simplest type of code to learn how to write and develop with. The readability of C++ code is weak in comparison and it is known as being a language that is a lot harder to get to grips with.

Learning Curve: Python is generally considered easier to learn for beginners due to its simplicity, while Java is more complex but provides a deeper understanding of how programming works. Performance: Java has a higher performance than Python due to its static typing and optimization by the Java Virtual Machine (JVM).

Python can be considered beginner-friendly, as it is a programming language that prioritizes readability, making it easier to understand and use. Its syntax has similarities with the English language, making it easy for novice programmers to leap into the world of development.

To start coding in Python, you need to install Python and set up your development environment. You can download Python from the official website, use Anaconda Python, or start with DataLab to get started with Python in your browser.

Learning Curve: Python is generally considered easier to learn for beginners due to its simplicity, while Java is more complex but provides a deeper understanding of how programming works.

Python alone isn't going to get you a job unless you are extremely good at it. Not that you shouldn't learn it: it's a great skill to have since python can pretty much do anything and coding it is fast and easy. It's also a great first programming language according to lots of programmers.

The point is that Java is more complicated to learn than Python. It doesn't matter the order. You will have to do some things in Java that you don't in Python. The general programming skills you learn from using either language will transfer to another.


Read on for tips on how to maximize your learning. In general, it takes around two to six months to learn the fundamentals of Python. But you can learn enough to write your first short program in a matter of minutes. Developing mastery of Python's vast array of libraries can take months or years.


6 Top Tips for Learning Python

  • Choose Your Focus. Python is a versatile language with a wide range of applications, from web development and data analysis to machine learning and artificial intelligence.
  • Practice regularly.
  • Work on real projects.
  • Join a community.
  • Don't rush.
  • Keep iterating.

The following is a step-by-step guide for beginners interested in learning Python using Windows.

  • Set up your development environment.
  • Install Python.
  • Install Visual Studio Code.
  • Install Git (optional)
  • Hello World tutorial for some Python basics.
  • Hello World tutorial for using Python with VS Code.

Best YouTube Channels to Learn Python

  • Corey Schafer.
  • sentdex.
  • Real Python.
  • Clever Programmer.
  • CS Dojo (YK)
  • Programming with Mosh.
  • Tech With Tim.
  • Traversy Media.

Python can be written on any computer or device that has a Python interpreter installed, including desktop computers, servers, tablets, and even smartphones. However, a laptop or desktop computer is often the most convenient and efficient option for coding due to its larger screen, keyboard, and mouse.

Write your first Python programStart by writing a simple Python program, such as a classic "Hello, World!" script. This process will help you understand the syntax and structure of Python code.

  • Google's Python Class.
  • Microsoft's Introduction to Python Course.
  • Introduction to Python Programming by Udemy.
  • Learn Python - Full Course for Beginners by freeCodeCamp.
  • Learn Python 3 From Scratch by Educative.
  • Python for Everybody by Coursera.
  • Learn Python 2 by Codecademy.

  • Understand why you're learning Python. Firstly, it's important to figure out your motivations for wanting to learn Python.
  • Get started with the Python basics.
  • Master intermediate Python concepts.
  • Learn by doing.
  • Build a portfolio of projects.
  • Keep challenging yourself.

Top 5 Python Certifications - Best of 2024
  • PCEP (Certified Entry-level Python Programmer)
  • PCAP (Certified Associate in Python Programmer)
  • PCPP1 & PCPP2 (Certified Professional in Python Programming 1 & 2)
  • Certified Expert in Python Programming (CEPP)
  • Introduction to Programming Using Python by Microsoft.

The average salary for Python Developer is β‚Ή5,55,000 per year in the India. The average additional cash compensation for a Python Developer is within a range from β‚Ή3,000 - β‚Ή1,20,000.

The Python interpreter and the extensive standard library are freely available in source or binary form for all major platforms from the Python website, https://www.python.org/, and may be freely distributed.

If you're looking for a lucrative and in-demand career path, you can't go wrong with Python. As one of the fastest-growing programming languages in the world, Python is an essential tool for businesses of all sizes and industries. Python is one of the most popular programming languages in the world today.

line

Copyrights © 2024 letsupdateskills All rights reserved