Python Topics : Commonly Used Built-in Decorators
@classmethod
class methods are methods that are bound to a particular class
these methods are not tied to any instances of the class
the @classmethod is a built-in decorator that is used to create a class method
class GFGClass:
  class_var = 10
  
  @classmethod
  def gfg_class_method(cls):
    print("This is a Class method")
    print(f"Value of class variable {cls.class_var}")

GFGClass.gfg_class_method()
@staticmethod
a static method is a method which is bound to the class and NOT the object of the class
the @staticmethod is a built-in decorator that is used to create a static method
class GFGClass:
  
  @staticmethod
  def gfg_static_method():
    print("This is a static method")

GFGClass.gfg_static_method()
@abstractmethod
abstract methods are declared inside an abstract class without any method definition
this method is meant to be implemented by the base class who implements the parent abstract class
the @abstractmethod decorator provided by abc module is used to implement abstract methods
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass


class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
      return self.side**2

square = Square(10)
print(square.area())
@atexit.register
the @atexit.register decorator is used to call the function when the program is exiting
the functionality is provided by the atexit module
can be used to perform the clean-up tasks before the program exits
import atexit

@atexit.register
def gfg_exit_handler():
  print("Bye, Geeks!")

print("Hello, Geeks!")
@typing.final
the @final decorator from the typing module is used to define the final class or method
final classes cannot be inherited or overridden
import typing

class Base:
    @typing.final
    def done(self):
        print("Base")

class Child(Base):
    def done(self):  # Error: Method "done" cannot override final method defined in class "Base" 
        print("Child")

@typing.final
class Geek:
    pass

class Other(Geek):  # Error: Base class "Geek" is marked final and cannot be subclassed
    pass
@enum.unique
Enumeration or Enum is a set of unique names that have a unique value
useful to define a set of constants that have a specific meaning
the @unique decorator provided by enum module is used to ensure that enumeration members are unique
from enum import Enum, unique

@unique
class Days(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 2 # ValueError: duplicate values found in <enum 'Days'>: THURSDAY -> TUESDAY
@property
Getters and Setters are used within the class to access or update the value of the object variable within that class
the @property decorator is used to define getters and setters for class attributes
class Geek:
    def __init__(self):
        self._name = 0

    @property
    def name(self):
      print("Getter Called")
      return self._name

    @name.setter
    def name(self, name):
      print("Setter called")
      self._name = name

p = Geek()
p.name = 10

print(p.name)
@enum.verify
the @enum.verify decorator in Python is used to ensure that the values of the members of an enumeration follow a particular pattern or constraint
some predefined constraints provided by the enum include
  • CONTINUES - the CONTINUES modules help ensure that there is no missing value between the highest and lowest value.
  • UNIQUE - the UNIQUE modules help ensure that each value has only one name
from enum import Enum, verify, UNIQUE, CONTINUOUS

@verify(UNIQUE)
class Days1(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 2 #ValueError: aliases found in <enum 'Days'>: THURSDAY -> TUESDAY

@verify(CONTINUOUS)
class Days2(Enum):
    MONDAY = 1
    TUESDAY = 2
    THURSDAY = 4 #ValueError: invalid enum 'Days': missing values 3
@singledispatch
a function with the same name but different behavior with respect to the type of argument is a generic function
the @singledispatch decorator is used to create a generic function
can create an overloaded function that can work with multiple types of parameters
from functools import singledispatch

@singledispatch
def geek_func(arg):
    print("Function Call with single argument")

@geek_func.register(int)
def _(arg):
    print("Function Called with an integer")

@geek_func.register(str)
def _(arg):
    print("Function Called with a string")

@geek_func.register(list)
def _(arg):
    print("Function Called with a list")

geek_func(1) 
geek_func([1, 2, 3]) 
geek_func("geek") 
geek_func({1: "geek1", 2: "geek2"})
@lru_cache
this decorator returns the same as lru_cache(maxsize=None) by creating a thin wrapper around a dictionary lookup for the function arguments
this is smaller and faster than lru_cache() with a size limit
never needs to remove old values
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

# First call to the function will execute the computation and cache the result
start_time = time.time()

result = fibonacci(10)
print(result)
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()

# Subsequent calls to the function with the same input will return the cached result
result = fibonacci(10)
print(result)
print("--- %s seconds ---" % (time.time() - start_time))
@dataclasses.dataclass
the @dataclasses.dataclass is a powerful decorator used to automatically generate common special methods for classes such as "__init__", "__repr__" and others
it helps write cleaner, more concise code
eliminates the need to write boilerplate methods for initializing and comparing instances of your class
can help prevent errors by ensuring that common special methods are implemented consistently across the codebase
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int


point = Point(x=3, y=2)
# Printing object
print(point)

# Checking for the equality of two objects
point1 = Point(x=1, y=2)
point2 = Point(x=1, y=2)
print(point1 == point2)
@partial
the partial decorator is a powerful tool that is used to create partial functions
partial functions allow pre-setting some of the arguments of the original function and generate a new function with those arguments already filled in
from functools import partial

# Original function
def power(base, exponent):
    return base ** exponent

# Creating a partial function with the exponent fixed to 2
square = partial(power, exponent=2)

# Using the partial function
result = square(3)
print("Output:",result) # 9
index