current position:Home>[Python] function decorators and common decorators

[Python] function decorators and common decorators

2022-02-01 11:24:51 Hazelnut

「 This is my participation 11 The fourth of the yuegengwen challenge 6 God , Check out the activity details :2021 One last more challenge

Function decorator

Function decorator is used in source code “ Mark ” function , Enhance the behavior of functions in some way .

Decorators are callable objects , Its argument is another function ( Decorated function ). Decorators may handle decorated functions , And then I'm going to return it , Or replace it with another function or callable object .

Take a chestnut :

def target():
	print('running target()')

#  amount to 
def target():
	print('running target()')

target = decorate(target)
 Copy code 

One of the major characteristics of decorators is , Can replace the decorated function with other functions . The second characteristic is , They run immediately after the decorated function definition ; This is usually done when importing ( namely Python When loading modules ).

Decorators are usually defined in a module , And then apply it to functions in other modules .

Most decorators define a function inside , Then return it to .

Decorators in standard library

Python There are three built-in functions for decorating methods :propertyclassmethod and staticmethod .

Another common ornament is functools.wraps , Its role is to help build a well behaved decorator . The two most noteworthy decorators in the standard library are lru_cache and singledispatch (Python 3.4 newly added ). These two ornaments are functools Defined in module .


functools.lru_cache Realized the memo (memoization) function . It saves the results of time-consuming functions , Avoid double calculation when the same parameter is passed in .LRU The three letters are “Least Recently Used” Abbreviation , Indicates that the cache will not grow unlimited , Cache entries that are not used for a period of time are thrown away .

lru_cache Two optional parameters can be used to configure . Its signature is :

functools.lru_cache(maxsize=128, typed=False)
 Copy code 

maxsize Parameter specifies how many call results are stored . When the cache is full , The old results will be thrown away , Make room . For the best performance ,maxsize It should be set to 2 The power of . typed If the parameter is set to True , Save the results of different parameter types separately , That is, the floating-point number and integer parameter which are usually considered equal ( Such as 1 and 1.0 ) Differentiate .

because lru_cache Use a dictionary to store results , And the key is created according to the location parameters and keyword parameters passed in during the call , So be lru_cache Functions of decoration , All its parameters must be hashable .


because Python Overloaded methods or functions are not supported , Therefore, we cannot use different signatures to define variants of functions with the same function name , You can't handle different data types in different ways . stay Python in , A common practice is to use a string of if/elif/elif , Call special functions , Such as functionA_strfunctionA_int , wait . This is not convenient for the user to expand the module , And clumsy : A long time , Dispatch function functionA It's going to get big , Moreover, it is closely coupled with various special functions .

functools.singledispatch The decorator can split the overall scheme into multiple modules , You can even provide special functions for classes that you can't modify . Use @singledispatch The ordinary function of decoration will become the universal function (generic function): According to the type of the first parameter , A set of functions that perform the same operation in different ways .

Special functions can be registered anywhere in the system and in any module .

Take a chestnut :

from functools import singledispatch
import numbers
import html

@singledispatch #  Tag handling  object  Base functions of types 
def htmlize(obj):
    content = html.escape(repr(obj))
    return '<pre>{}</pre>'.format(content)

@htmlize.register(str) #  Each specialized function is used  @«base_function».register(«type»)  decorate .
def _(text):			#  The name of the specialized function does not matter ;_  It's a good choice 
    content = html.escape(text).replace('\n', '<br>\n')
    return '<p>{0}</p>'.format(content)

def _(n):
    return '<pre>{0} (0x{0:x})</pre>'.format(n)
 Copy code 

Parametric decorators

Let the decorator accept other parameters : Create a decorator factory function , Pass the parameters to it , Returns a decorator , Then apply it to the function to decorate .

Take a chestnut :

registry = set()

def register(active=True):		#  Accept an optional keyword parameter 
    def decorate(func):			#  The real decorator 
        print('running register(active=%s)->decorate(%s)' % (active, func))
        if active:		#  Only  active  The value of the parameter ( Get... From a closure ) yes  True  Register only when  func
        else:			#  If  active  Not true , and  func  stay  registry  in , Then delete it 
        return func		# decorate  It's a decorator , Must return a function 
    return decorate

@register(active=False) # @register  Factory functions must be called as functions , And pass in the required parameters 
def f1():
	print('running f1()')
@register() #  Even if no parameters are passed in ,register  Must also be called as a function , That is to return to the real decorator  decorate
def f2():
	print('running f2()')
 Copy code 

copyright notice
author[Hazelnut],Please bring the original link to reprint, thank you.

Random recommended