Python Decorators - coderz.py https://coderzpy.com Keep Coding Keep Cheering! Tue, 17 Jan 2023 08:53:07 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://coderzpy.com/wp-content/uploads/2022/08/cropped-image1-1-32x32.jpg Python Decorators - coderz.py https://coderzpy.com 32 32 Python Decorators https://coderzpy.com/python-decorators/ https://coderzpy.com/python-decorators/#respond Tue, 17 Jan 2023 08:19:46 +0000 http://coderzpy.com/?p=2241 In Python, a decorator is a special kind of function that modifies the behavior of another function. Decorators are often ...

The post Python Decorators first appeared on coderz.py.

]]>
In Python, a decorator is a special kind of function that modifies the behavior of another function. Decorators are often implemented as closures, which are nested functions that can remember and access variables from their containing function, even after the containing function has finished executing.

But before diving deep into decorators let us understand some concepts that will come in handy in learning the decorators.

First Class Objects:

Functions in Python are first-class objects, which means they have the same properties and capabilities as any other object, such as integers or strings. This allows for greater flexibility and power in programming, as functions can be passed as arguments to other functions, returned from other functions, stored in data structures, and used in other ways that are typically reserved for regular objects.

Decorators:

As previously stated, decorators are used to modify the behavior of a function or class. Decorators use functions as arguments in another function, which is then called inside the wrapper function.

A decorator is applied to a function using the @decorator_name notation, which is a shorthand for calling decorator_name(function) and assigning the result back to the function.

Here is an example of a decorator function that adds logging to a function:

import logging

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} returned: {result}")
        return result
    return wrapper

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

add(1, 2)
</code>

In this example, the log_decorator function takes a function as an argument and returns a wrapper function that logs the function call and its result. The @log_decorator syntax is used to apply the decorator to the add() function. When the add(1, 2) is called, it will first log the function call, execute the original add function and then log the result.

When the code runs, it will output something like this:

INFO:root:Calling function add with args: (1, 2), kwargs: {}
INFO:root:Function add returned: 3

This is a simple example, but decorators can be used in many ways to add functionality to functions and classes in a clean, reusable way.

Chaining the Decorators:

We can chain multiple decorators together to apply multiple modifications to a function. Here’s an example:

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Time taken: {end_time - start_time}")
        return result
    return wrapper

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} returned: {result}")
        return result
    return wrapper

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

add(1, 2)

In this example, both timer_decorator and log_decorator are applied to the add() function using the @ syntax. So, when the add(1, 2) is called, it will first log the function call, then it will execute the original add function, measure the execution time and then log the result.

The order of decorators is important, as it determines the order in which the decorators’ wrapper functions are executed. In this example, log_decorator is applied first and timer_decorator is applied second, so logging happens before timing. If you want to change the order, you can swap the decorators like this:

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

This way, the timing will happen before the logging.

Note: also read about Python Closures

Follow Me

Please follow me to read my latest post on programming and technology if you like my post.

https://www.instagram.com/coderz.py/

https://www.facebook.com/coderz.py

The post Python Decorators first appeared on coderz.py.

]]>
https://coderzpy.com/python-decorators/feed/ 0
@property Decorator in Python https://coderzpy.com/property-decorator-in-python/ https://coderzpy.com/property-decorator-in-python/#respond Tue, 17 Jan 2023 08:53:02 +0000 http://coderzpy.com/?p=2243 The @property decorator in Python is used to define a method as a “getter” for the boundaries of a class ...

The post @property Decorator in Python first appeared on coderz.py.

]]>
The @property decorator in Python is used to define a method as a “getter” for the boundaries of a class attribute. It allows you to define a method that is accessed like an attribute, without needing to invoke it like a method.

Here’s an example:

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

c = Circle(5)
print(c.radius) # prints 5
c.radius = 10
print(c.radius) # prints 10

In this example, the @property decorator is used to define the radius method as a getter for the _radius attribute. The @radius.setter decorator is used to define a setter method for the radius property, which allows you to change the value of the _radius attribute.

This way, you can use the Circle.radius as a variable without invoking it like a method, it would make it look like an attribute rather than a method.

Also, It is worth noting that, property decorator can also be used to define a method as a “setter” for a class attribute using @property_name.setter

Using @property Decorator:

Using the @property decorator can make your code more readable and maintainable by allowing you to access class attributes like properties, rather than having to invoke methods. It also allows you to add logic and validation to the getter and setter methods, which can help ensure that the attribute is always in a valid state.

Here are some best practices for using the @property decorator:

  • Use it to define getter methods for class attributes, especially when the attribute is a calculated value or has some logic behind it.
  • Use the @property_name.setter decorator to define setter methods for class attributes, which allows you to add validation and logic to the attribute setter.
  • Use private attributes (e.g. _radius) to store the actual value, and use properties to expose it to the outside world.
  • Be careful not to add too much complexity to the getter and setter methods, as it can make the code harder to understand and maintain.
  • When using the @property decorator, it is a good idea to add a docstring to the property method explaining what it does and how it should be used.
  • Use @property and @property_name.setter decorators together to define a read-only property by omitting the setter method.

Overall, the @property decorator is a powerful tool that can help you write more maintainable and readable code, but it should be used with care and with consideration for its impact on the overall design of your codebase.

Defining setter and deleter methods with @property Decorator:

Similarly to the getter method (which we defined in the previous example), we can define setter and deleter methods for any attribute in Python that uses @property.

The setter method will set the attribute’s value, and the deleter method will remove the attribute from memory.

Let’s add a setter and deleter method to our fullname attribute. We use a special syntax to define a setter and deleter method for an attribute with @property decorator set on its getter method, where we put @ATTRIBUTE NAME.setter and @ATTRIBUTE NAME.deleter decorators on function with the same name as the ATTRIBUTE NAME.

Python property() function:

The Python property() function returns the object of the property class and is used to create class properties.

Syntax: property(fget, fset, fdel, doc)

Parameters: 

  • fget() – used to get the value of attribute
  • fset() – used to set the value of attribute
  • fdel() – used to delete the attribute value
  • doc() – string that contains the documentation (docstring) for the attribute

Return: Returns a property attribute from the given getter, setter and deleter.

Note:

  • If no arguments are provided, the property() method returns a base property attribute with no getter, setter, or deleter.
  • If doc is not provided, the property() method uses the getter function’s docstring.
Example:

Here’s an example of using the property() method to define a class attribute called age that can be read and written, but only allows positive integer values:

class Person:
    def __init__(self):
        self._age = None

    def get_age(self):
        return self._age

    def set_age(self, value):
        if isinstance(value, int) and value > 0:
            self._age = value
        else:
            raise ValueError("Age must be a positive integer")

    age = property(get_age, set_age)

person = Person()
person.age = 30
print(person.age) # prints 30

person.age = -5 # raises ValueError: Age must be a positive integer

In this example, the get_age() method is defined as the getter for the age attribute, and the set_age() method is defined as the setter. The age = property(get_age, set_age) line creates the age attribute and sets the getter and setter methods. The set_age() method checks the value passed to it, if it is a positive integer it assigns the value to _age else raises an exception.

Note: also read about Python Decorators

Follow Me

Please follow me to read my latest post on programming and technology if you like my post.

https://www.instagram.com/coderz.py/

https://www.facebook.com/coderz.py

The post @property Decorator in Python first appeared on coderz.py.

]]>
https://coderzpy.com/property-decorator-in-python/feed/ 0