Question: function to add a layer of packaging, additional processing has been added, for example, logging, statistics and other timing solutions: You can define a decorator to wrap function
Problem: When a function is decorative decorator, a number of important metadata such as: name of the function, documentation string, the function call notes and signatures have been lost Solution: Whenever define a decorator should always remember as the underlying Add functools wrapper function library @wraps decorator
# Problem: When a function is decorative decorator, a number of important metadata such as: name of the function, documentation string, the function call notes and signatures were missing # Solution: Whenever define a decorator should always remember to underlying wrapper function added functools library @wraps decorator Import Time Import functools DEF timethis (FUNC): @ functools.wraps (FUNC) DEF warpper (* args, ** kwargs): START_TIME = the time.time () Result = FUNC (* args, ** kwargs) END_TIME = the time.time () Print (FUNC. the __name__ , end_time- START_TIME) return Result return warpper @timethis DEFmysleep (NUM: int): "" " primitive Comments Document : param NUM: : return: " "" the time.sleep (NUM) Print ( " I am the original function " ) mysleep ( 3 ) Print (mysleep. __name__ ) Print (mysleep. __doc__ ) Print (mysleep. __annotations__ ) # If decorators use @ functools.wraps (func) decoration, we can use the following method to get to the original function! ! ! mysleep. __wrapped__ (. 3)
Question: We have added a decorator to function, but would like to withdraw it, access the original function without packaging. Solution: Assuming decorator has achieved @warps (func), in general we can get to the original function by accessing the property __wrapped__
Question: We want to write a decorator function can receive parameters Solution: Suppose we want to write a decorator to add logging to function, but it allows the user to specify the level of logging as well as some other details of the operation as a parameter.
import logging import functools def logged(level,name=None,message=None): def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @functools.wraps(func) def wrapper(*args,**kwargs): log.log(level,message) return func(*args,**kwargs) return wrapper return decorate
Question: We want to write a decorator to wrap function, but allows the user to adjust the properties decorator, so at runtime can control the behavior of the decorator
from functools import wraps,partial import logging def attach_wrapper(obj,func=None): if func is None: return partial(attach_wrapper,obj) setattr(obj,func.__name__,func) return func def logged(level,name=None,message=None): def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args,**kwargs): log.log(level,logmsg) return func(*args,**kwargs) @attach_wrapper(wrapper) def set_level(newlevel): nonlocal level level = newlevel @attach_wrapper(wrapper) def set_message(newmsg): nonlocal logmsg logmsg = newmsg return wrapper return decorate logging.basicConfig(level=logging.DEBUG) @logged(logging.DEBUG) def add(x,y): return x + y add(2,5) add.set_message("Add called") add(3,8)
Problems: We want to write a separate decorator, so that it may be no parameter like @decorator, optional parameters may be received so as @decorator (x, y, z)
from functools import wraps,partial import logging def logged(func=None,*,level=logging.DEBUG,name=None,message=None): if func is None: return partial(logged,level=level,name=name,message=message) logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args,**kwargs): log.log (Level, logmsg) return FUNC (* args, ** kwargs) return warpper @logged DEF the Add (X, Y): logging.debug ( " hahahah " ) return X + Y # no parameters, decorative it is equivalent to: logged (func), so the first parameter decorator is func, others are optional parameters @logged (Level = logging.CRITICAL, name = " Example " ) DEF from spam (): Print ( " !!! from spam " ) # With an argument, decorator equivalent logged (Level = logging.DEBUG, name =" Example ") (from spam) #Clever use of the constructed functools.partial the method returns to the Add ( 1,2 ) from spam ()
Question: We would like to add a mandatory check function as a function parameter, as an assertion or contract between the caller
from Inspect Import Signature from functools Import Wraps DEF typeassert (ty_args *, ** ty_kwargs): DEF the decorate (func): IF Not __debug__ : return func SIG = Signature (func) # acquired func parameter signature (x, y, z) = sig.bind_partial bound_types (ty_args *, ** ty_kwargs) .arguments # parameter parameter type signature do mapping [( "x", <class "int">), ( "z", <class "int">)] @wraps (FUNC) DEF wrapper (* args, ** kwargs): bound_valuesSig.bind = (* args, ** kwargs) .arguments # parameter signature function arguments do mapping for name, value in bound_values.items (): IF name in bound_types: # determine whether there are restrictions on the type parameter IF not isinstance (value , bound_types [name]): The raise TypeError ( " the Argument MUST BE {} {} " .format (name, bound_types [name])) return FUNC (* args, ** kwargs) return warpper return the decorate class A (): DEF A (Self): Print (" A " ) @typeassert (int, A, Z = int) DEF the Add (X, Y, Z): Print (X, Y, Z) return X the Add ( . 1, A (),. 3 ) # idea: Parameter Type limiting parameters may be used in the processing method of front end of the received inspection parameters, needs to be limited it can also be used in some parts of the incoming parameter type # Note: this decorator a delicate place, only check the transmission parameters, if default parameters, not passed, the parameter type is not checked @typeassert (int, List) DEF bar (X, items = None): IF items IS None: items = [] items.append (X) return items Print (bar ( 2))
Question: We want a decorator class definition, and the effect on other functions or methods
from functools import wraps class A: def decorator1(self,func): @wraps(func) def wrapper(*args,**kwargs): print("decorator 1") return func(*args,**kwargs) return wrapper @classmethod def decorator2(cls,func): @wraps(func) def wrapper(*args,**kwargs): print("decorator 2") returnFUNC (* args, ** kwargs) return wrapper # ponder: @property actually have a getter (), setter (), class deleter () method, each method can be used as a decorator # several decorator You can manipulate the state of the instance, and therefore, if desired decorative or combining the information recorded by the back, this is a very sensible approach.
Question: We want to wrap the decorator function, but I hope the result is an instance of a callable. We need decorator both class work, it can also be used outside of the class solution: put decorator defined as a class instance, you need to ensure __call __ () and __get __ () method in the class
import types from functools import wraps class Profield: def __init__(self,func): wraps(func)(self) self.ncalls = 0 def __call__(self, *args, **kwargs): self.ncalls +=1 return self.__wrapped__(*args,**kwargs) def __get__(self, instance, cls): if instance is None: return self else: returntypes.MethodType (Self, instance) # The decorator means adding a property as a function ncalls
Question: We want to apply a static method on the class or decorator before will be simple and direct effect on the decorators to class and static methods, but to ensure decorator when the application needs to be placed @classmethod and @staticmethod: Solutions , for example:
import time from functools import wraps def timethis(func): @wraps(func) def wrapper(*args,**kwargs): start = time.time() r = func(*args,**kwargs) end = time.time() print(end-start) return r return wrapper
@classmethod and @staticmethod decorator and does not return an executable object, decorators should be placed below them! ! !
Question: We want to write a decorator, add additional parameters as the wrapped function, but does not affect the parameters added to the existing convention call the function solution:
from functools import wraps def optinoal_debug(func): @wraps(func) def wrapper(*args,debug=False,**kwargs): if debug: print("Calling",func.__name__) return func(*args,**kwargs) return wrapper
Function parameters are decorated portion used to parse the remaining arguments to the function, can be used to control the parameters of the package decoration function behavior
# Question: We want to check or rewrite the definition part of a class, in order to modify the behavior of the class, but do not want to through inheritance or metaclasses way to do # Solution:
def log_getattribute(cls): orig_getattribute = cls.__getattribute__ def new_getattribute(self,name): print("getting",name) return orig_getattribute(self,name) cls.__getattribute__ = new_getattribute return cls @log_getattribute class A: def __init__(self,x): self.x = x def spam(self): pass a = A(42) a.x
This method can be done by monitoring of the properties of the class