Decorative Device, in short, adds additional functionality to the function without altering any code of the original function, including the invocation. What is special is that the return value of the adorner is also a function.first, the introduction of the decorative device
Let's start with a simple chestnut:
Import Time
def count_time (foo_func):def inner ():print ("inner func start!")start_time = Time.time ()Time.sleep (1) # 1s after executionFoo_func ()end_time = Time.time ()print ("The Func cost:{:.2f}". Format (end_time-start_time))return inner
@count_timedef foo ():# Use adorner to calculate Foo function execution Timeprint ("foo execute done!!")
if __name__ = = ' __main__ ':foo () # Call the Foo function
since the addition of the adorner, the original function of the code and the way the call has not changed. where @count_time is equivalent to foo = Count_time (foo)
If you want to pass parameters to the adorner. . as follows:Import Time
def outer (name):def count_time (foo_func):def inner ():print ("inner func start!")print (the "adorner passed in parameter is: {}". Format (name))start_time = Time.time ()Time.sleep (1) # 1s after executionFoo_func ()end_time = Time.time ()print ("The Func cost:{:.2f}". Format (end_time-start_time))return innerreturn Count_time
@outer (name= "Jack")def foo ():# Use adorner to calculate Foo function execution Timeprint ("foo execute done!!")
if __name__ = = ' __main__ ':foo () # Call the Foo function
To pass parameters to the adorner, simply add a layer of function to the function.
However, some of the built-in variables of the function, such as __name__,__doc__, can be changed by using adorners. Look at chestnuts:
Import TimeFrom functools Import wraps
def count_time (foo_func):# @wraps (foo_func)def inner (): """Now in inner func """print ("inner func start!")start_time = Time.time ()Time.sleep (1) # 1s after executionFoo_func ()end_time = Time.time ()print ("foo func cost:{:.2f}". Format (end_time-start_time))return inner
@count_timedef foo (): """Now in foo func """# Use adorner to calculate Foo function execution Timeprint ("foo func doc is {}". Format (foo.__doc__)) # Output now in inner funcprint ("foo func name is {}". Format (foo.__name__)) # output Innerprint ("foo execute done!!")
if __name__ = = ' __main__ ':foo () # Call the Foo functionSo what does it take to correct it back? Come to
only need to introduce the wraps function under the Functools moduleuse @wraps (func) above the inner function to decorate the inner function againThe output statement will then change to:print ("foo func doc is {}". Format (foo.__doc__)) # output now in foo funcprint ("foo func name is {}". Format (foo.__name__)) # output Foo
to pass parameters to the decorated function:chestnuts are as follows:Import TimeFrom functools Import wraps
def count_time (foo_func):@wraps (func)def inner (*args, **kwargs): """Now in inner func """print ("inner func start!")start_time = Time.time ()Time.sleep (1) # 1s after executionFoo_func (*args, **kwargs)end_time = Time.time ()print ("foo func cost:{:.2f}". Format (end_time-start_time))return inner
@count_timedef foo (name, age, *args, **kwargs): """Now in foo func """# Use adorner to calculate Foo function execution Timeprint (name,age)print (Args,kwargs)print ("foo execute done!!")
if __name__ = = ' __main__ ':foo ("Jack", "Student", "department=") # Call the Foo function
the added place has four parameters for the call, namely the positional and keyword parameters (the last is the keyword argument, the keyword parameter must be passed in after the position parameter)The parameters of the inner function inside the adorner add *args, **kwargs, and inner internal func (*args, **kwargs) hereargs means that all positional parameters are inside the args tuple.Kwargs means that all the keyword parameters are in the dictionary of args.
second, the class-based implementation of the adorner look at chestnuts .class Logging (object):def __init__ (Self, func):Self.func = Func
def __call__ (self, *args, **kwargs):print ("[DEBUG]: Enter function {func} ()". Format (func=self.func.__name__))return Self.func (*args, **kwargs)
@Loggingdef Say (something):print ("Say {}!"). Format (something))
Say ("Hello")
Output:[DEBUG]: Enter function say ()say hello!Answer: If you implement an adorner with a class, you must implement the built-in methods in the class __init__ and __call__ both methodswhere __init__ is used to receive the name of the decorated function, __call__ is used to decorate the function and add additional functions.
to pass parameters to the adorner. or look at the chestnuts:class Logging (object):def __init__ (self, level= ' INFO '):Self.level = level
def __call__ (Self, func): # Accept functiondef wrapper (*args, **kwargs):print ("[{level}]: Enter function {func} ()". Format (Level=self.level,func=func.__name__))func (*args, **kwargs)
Return Wrapper # returns function
@logging (level= ' INFO ')def Say (something):print ("Say {}!"). Format (something))
Say ("Hello")
The call here is to pass parameters to the adorner. The __init__ method parameter is no longer the function name of the decorated function, but the parameter from the adorner.the __call__ method parameter receives the function name of the decorated function, and then nested a layer function to receive the parameters of the decoration function .
Finally, expand the wrapt this decorator :Import wrapt
@wrapt. Decoratordef logging (wrapped, instance, args, Kwargs): # instance is must needprint ("[DEBUG]: Enter {} ()". Format (wrapped.__name__))return Wrapped (*args, **kwargs)
@loggingdef Say (something): Pass
Say ("Hello")
This looks like the adorner is clearer, without nesting functions inside the adorner. the above must be written according to the abovewhere wrapped represents the function of being decorated,To pass parameters to the adorner, it is also possible to nest one layer at a later level.
Python's solution Decorator