Python knowledge: Effective use of property decorators

1. Description

        Python is the only language with idioms . This enhances its readability and perhaps its beauty. Decorators follow the Zen of Python , aka the "Pythonic" way. Decorators are available starting in Python 2.2. PEP318 enhances them. Below is a beginner-focused tutorial on how to use decorators. If you'd like to run the code examples yourself, keep reading! 

2. Decoration

        A (not to be confused with the Decorator pattern) is a way to add/change the behavior of a function without changing the original function. In Python, a decorator is a design pattern that allows you to modify the functionality of a function by wrapping it in another function. External functions are called decorators, which take the original function as argument and return a modified version.decorator

Let us lead by example. Here we declare a decorator. It helps to print the output of a function instead of adding print commands which later become unwieldy and sometimes very tedious to remove.debug


def debug(function):
    def wrapper(name, address):
      print ('Debugging:')
      func = function(name, address)
      print (func)
    return wrapper

@debug
def typical_crunching_function(name, city):
  return 'You are '+ name + ' from '+ city

typical_crunching_function('John','Los Angeles')

Output:

Debugging:
You are John from Los Angeles 

Here we define the decorator and apply it to the function using @ syntax.line 1-6typical_crunching_function line 8

3. Python class decorator

Class decorators were introduced in PEP3129. This was after a lot of resistance from the community, which they preferred. The main purpose was to extend the ability to decorate functions to classes.metaclasses

Below is an example of a class decorator that enhances the functionality of a function.

class Accolade:
  def __init__(self, function):
    self.function = function
    
  
  def __call__ (self, name):
    # Adding Excellency before name
    name = "Excellency " + name
    self.function(name)
    # Saluting after the name
    print("Thanks "+ name+ " for gracing the occasion")

@Accolade
def simple_function(name):
  print (name)

simple_function('John McKinsey')

Output:

Excellency John McKinsey
Thanks Excellency John McKinsey for gracing the occasion

        Classes are defined here that can be used to perform preprocessing and postprocessing on simple_function. In this example, we're just adding to the string, and after printing the names, thank them for honoring the occasion. This simple example demonstrates that we can easily perform pre- and post-processing on function arguments using class decorators. These preprocessing tasks can be any of the following, but are not limited to these.CleanerExcellencyname,

  • Add timing information
  • Connect to database
  • close connection
  • memory storage

Example 2:


class Decorator(object):
 
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.__doc__ = doc
 
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.fget(instance)
 
    def __set__(self, instance, value):
        self.fset(instance, value)
 
    def __delete__(self, instance):
        self.fdel(instance)
 
    def getter(self, fget):
        return Decorator(fget, self.fset, self.fdel, self.__doc__)
 
    def setter(self, fset):
        return Decorator(self.fget, fset, self.fdel, self.__doc__)
 
    def deleter(self, fdel):
        return Decorator(self.fget, self.fset, fdel, self.__doc__)
 
 
class Target(object):
    desc = "Amazing pyhton"
 
    def __init__(self, attr=5):
        self._x = attr
    @Decorator
    def show(self):
        return self._x
 
    @show.setter
    def show(self, value):
        self._x = value
 
    @show.deleter
    def show(self):
        del self._x

4. Some built-in class decorators

        Below are some built-in class decorators.

4.1 Cache/memory

        Note that it is only available in Python >= 3.9. This allows previous values ​​to be cached and reused instead of recalculating them.

from functools import cache

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

print(factorial(10))

Output:

3628800

4.2 Properties

The meaning of property

@property turns the getter method of a class into a property. If there is a setter method, add @method.setter in front of the setter method. Using class property = property(getx, setx, delx, desc) is also possible.

The implementation is simple, so what is the principle behind it?

The pseudocode of the Property class is as follows, which involves the __get__, __set__, and __delete__ magic methods. The Decorator class is the decorator class, and Target is the target class. When you set the instance object of the decorator class as the x attribute of the target class, when you try to access the x attribute of the target class, the __get__ method of the decorator class will be triggered; when you assign a value to the x attribute of the target class, the decorator will be triggered The __setter__ method of the class; when trying to delete the x attribute of the target class, the __delete__ method of the decorator class is triggered. When accessing Target.x.__doc__, the description document of the decorator class can be printed. In fact this decorator class is also called a descriptor class. A descriptor class is an attribute that assigns an instance of a special class to a class.

Class attribute implementation:
Example 1:

        This decorator allows adding setter and getter functions to properties in a class.

class Pencil:
  def __init__(self, count):
    self._counter=count
  
  @property
  def counter(self):
    return self._counter
  
  @counter.setter
  def counter(self, count):
    self._counter = count

  @counter.getter
  def counter(self):
    return self._counter

HB = Pencil(100)
print (HB.counter)
HB.counter = 20
print (HB.counter)

Output:

100
20 

Example 2:


class Decorator(object):
 
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        self.__doc__ = doc
 
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self.fget(instance)
 
    def __set__(self, instance, value):
        self.fset(instance, value)
 
    def __delete__(self, instance):
        self.fdel(instance)
 
    def getter(self, fget):
        return Decorator(fget, self.fset, self.fdel, self.__doc__)
 
    def setter(self, fset):
        return Decorator(self.fget, fset, self.fdel, self.__doc__)
 
    def deleter(self, fdel):
        return Decorator(self.fget, self.fset, fdel, self.__doc__)
        
 
class Target(object):
    
    desc = "Amazing pyhton"
    
    def __init__(self, attr=5):
        self._x = attr
    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
 
    x = Decorator(getx,setx,delx,desc)

 

4.3 Cache property cached_property

This decorator allows caching of properties of a class. This is equivalent to nesting two decorators.

@cached
@property
def counter:
  return self._counter

5. Conclusion

        Decorators are very convenient and elegant tools, however, they are also very error-prone. Therefore, they should be used with great caution. Check out the course below to get hands-on experience with Python decorators!  Education team

Guess you like

Origin blog.csdn.net/gongdiwudu/article/details/122642370