SoFunction
Updated on 2024-11-20

Python decorator application scenarios code summary

Application Scenarios for Decorators

  • additional functionality
  • Data cleansing or addition.
    • Function parameter type validation @require_ints Similar pre-request interception
    • Data format conversion Change function return dictionary to JSON/YAML similar to response post tampering
    • Providing additional data for functions
  • function registration
    • Register a task in the Task Center
    • Registering a function with a signal handler

Decorator implementation for different application scenarios

function registry

simple registry

funcs = []
def register(func):
  (func)
  return func    
@register
def a():
  return 3
  
@register
def b():
  return 5  
# Access results
result = [func() for func in funcs]

Registry isolation (using different instances of a class)

class Registry(object):
  def __init__(self):
    self._funcs = []
  
  def register(self, func):
    self._funcs.append(func)
    
  def run_all(self):
    return [func() for func in self._funcs] 
r1 = Registry()
r2 = Registry()

@
def a():
  return 3
  
@
def b():
  return 5
  
@
@

Execution-time encapsulation code

Type checking

from functools import wraps
def require_ints(func):
  @wraps(func) # Copy the info from func to inner
  def inner(*args, **kwargs):
    for arg list(args) + list(()):
      if not isinstance(arg, int:
        raise TypeError("{} Accept onlyinttype parameter".format(func.__name__)
    return func(*args, **kwargs)
  return inner

user authentication

from functools import wraps

class User(object):
  def __init__(self, username, email):
     = username
     = email
    
class AnonymousUser(object):
  def __init__(self):
     =  = None
  def __nonzero__(self): # Called when converting an object to a bool type
    return False
def requires_user(func):
  @wraps(func)
  def inner(user, *args, **kwargs): # This decorator does not support decorated classes because self is not supported in the first argument.
    if user and isinstance(user, User):
      return func(use, *args, **kwargs)
    else:
      raise ValueError("Non-legal users")
  return inner

Output Formatting

import json
from functools import wraps
def json_output(func): # Convert the dictionary format returned by func to a json string.
  @wrap(func)
  def inner(*args, **kwargs):
    return (func(*args, **kwargs))
  return inner

exception capture

import json
from functools import wraps

class Error1(Exception):
  def __init__(self, msg):
     = msg
  def __str__(self):
    return 
    

def json_output(func):
  @wrap(func)
  def inner(*args, **kwargs):
    try:
      result = func(*args, **kwargs)
    except Error1 as ex:
      result = {"status": "error", "msg": str(ex)}
    return (result)
  return inner
# Usage
@json_ouput
def error():
  raise Error1("The exception is caught and output in JSON format.")

Log Management

import time
import logging
from functools import wraps

def logged(func):
  @wraps(func)
  def inner(*args, **kwargs): # *args can decorate functions as well as classes
    start = ()
    result = func(*args, **kwargs)
    exec_time = () - start
    logger = ("")
    ("{} invocation time:{:.2} execution time:{:.2}s in the end:{}".format(func.__name__, start, exec_time, result)

Decorator with parameters

Decorator with parameters is equivalent to a function that returns a decorator, @deco(a=1) will first execute deco(a=1) to get an actual decorator before calling @, Decorator with parameters deco(a=1) is executed immediately upon module import.

decorative category

Add sortable functionality to classes (without extending the parent's methods by inheriting from the subclasses, e.g., if multiple classes need to add this functionality).

import time
from functools import wraps
def sortable_by_created(cls):
  original_init = cls.__init__
  @wrap(original_init)
  def new_init(self, *args, **kwargs):
    original_init(*args, **kwargs)
    self._created = ()
  cls.__init__ = new_init
  
  cls.__lt__ = lambda self, other: self._created < other._created
  cls.__gt__ = lambda self, other: self._created > other._created
  return cls

A SortableByCreated() class can also be defined, with children using multiple inheritance from their parent and SortableByCreated.

type conversion

Functions may become instances of a class after being decorated, in which case the __call__ method should be provided for the returned class in order to be compatible with the function call.

class Task(object):
  def __call__(self, *args, **kwargs):
    return (*args, **kwargs)
  def run(self, *args, **kwargs):
    raise NotImplementedError("The subclass does not implement the interface.")
def task(func):
  class SubTask(Task):
    def run(self, *args, **kwargs):
      func(*args, **kwargs)
  return SubTask()

Chapter 2 Context Manager

define

Packaging arbitrary code

Ensuring consistency in implementation

grammatical

with statement

__enter__ and __exit__ methods

class ContextManager(object):
  def __init__(self):
     = False    
  def __enter__(self):
     = True
    return self    
  def __exit__(self, exc_type, exc_instance, traceback):
     = False

application scenario

Resource cleanup

import pymysql

class DBConnection(object):
  def __init__(self, *args, **kwargs):
    , = args, kwargs
    
  def __enter__(self):
     = (*args, **kwargs)
    return ()
    
  def __exit__(self, exc_type, exc_instance, trackback):
    ()

Exception handling (avoid duplication)

Propagate exception (return False in __exit__)

Terminate exception (return True in __exit__)

class BubleExceptions(object):
  def __enter__(self):
    return self
  def __exit__(self, exc_type, exc_instance, trackback):
    if exc_instance:
      print("abnormalities: {}".format(exc_instance)
    return False  # return TrueTermination exceptions

Handling specific exceptions

class HandleValueError(object):
  def __enter__(self):
    return self
  def __exit__(self, exc_type, exc_instance, trackback):
    if not exc_type: return True
    if issubclass(exc_type, ValueError): 
      print("deal withValueError: {}".format(exc_instance)
    return False

if issubclass... If exec_type == ValueError then ValueType subclass exceptions are not handled.

It is also possible to determine whether to propagate or terminate based on the properties of the exception

Simpler syntax

import contextlib

@
def acceptable_error_codes(*codes):
  try:
    yield
  except ShellException as exc_instance:
    if exc_instance.code not in codes:
      raise
    pass

This is the whole content of this article.