1. Introduction: Functions are also objects
A function with wooden brackets is not being called.
def hi(name="yasoob"): return "hi " + name print(hi()) # output: 'hi yasoob' # We can even assign a function to a variable, such as greet = hi # We're not using parentheses here because we're not calling the hi function # # Instead, we're putting it in the greet variable. Let's try running this print(greet()) # output: 'hi yasoob' # If we delete the old hi function and see what happens! del hi print(hi()) #outputs: NameError print(greet()) #outputs: 'hi yasoob'
2. Functions within functions
(1) In python, a function can define another function nested within it, and can call that smaller function within that larger function.
def hi(name="yasoob"): print("now you are inside the hi() function") def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" print(greet()) print(welcome()) print("now you are back in the hi() function") hi() #output:now you are inside the hi() function # now you are in the greet() function # now you are in the welcome() function # now you are back in the hi() function # The above shows that whenever you call hi(), greet() and welcome() will be called at the same time. # Then the greet() and welcome() functions are not accessible outside of the hi() function, for example: greet() #outputs: NameError: name 'greet' is not defined
(2) What starts the magic is that the return value of a large function can be a function:
def hi(name="yasoob"): def greet(): return "now you are in the greet() function" def welcome(): return "now you are in the welcome() function" if name == "yasoob": return greet #Here! else: return welcome a = hi() print(a) #outputs: <function greet at 0x7f2143c01500> # The above clearly shows that `a` now points to the greet() function within the hi() function # Now try this print(a()) #outputs: now you are in the greet() function
Instead of greet() and welcome(), we return greet and welcome in an if/else statement.
Why is that? It's because when you put a pair of parentheses after it, the function executes; however, if you don't put parentheses after it, then it can be passed around and assigned to other variables without executing it.
When we write a = hi(), hi() is executed, and since the name argument defaults to yasoob, the function greet is returned.
PS: If we print out hi()(), this will output now you are in the greet() function.
(3) The last thing to say is that a function is passed as an argument to a function:
def hi(): return "hi yasoob!" def doSomethingBeforeHi(func): print("I am doing some boring work before executing hi()") print(func()) doSomethingBeforeHi(hi) #outputs:I am doing some boring work before executing hi() # hi yasoob!
3. Decorator Little Chestnut
Finally, we come to decorators with @, which are actually functions with @ hats as arguments that are passed into the function following the @.
def a_new_decorator(a_func): def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): """Hey you! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell") a_function_requiring_decoration() #outputs: I am doing some boring work before executing a_func() # I am the function which needs some decoration to remove my foul smell # I am doing some boring work after executing a_func() #the @a_new_decorator is just a short way of saying: a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
The code above is equivalent to the familiar:
def a_new_decorator(a_func): def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction def a_function_requiring_decoration(): print("I am the function which needs some decoration to remove my foul smell") a_function_requiring_decoration() #outputs: "I am the function which needs some decoration to remove my foul smell" a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration) #now a_function_requiring_decoration is wrapped by wrapTheFunction() a_function_requiring_decoration() #outputs:I am doing some boring work before executing a_func() # I am the function which needs some decoration to remove my foul smell # I am doing some boring work after executing a_func()
However, the name of the function that was decorated at the beginning has been quietly "changed", as you can see if you print it (in the following code).
Solution:
@wraps accepts a function for decoration and adds the ability to copy the function name, comment documentation, parameter list, etc. This allows us to access the properties of the function before it was decorated from inside the decorator.
print(a_function_requiring_decoration.__name__) # Output: wrapTheFunction
The final code with @wraps is as follows:
from functools import wraps def a_new_decorator(a_func): @wraps(a_func) def wrapTheFunction(): print("I am doing some boring work before executing a_func()") a_func() print("I am doing some boring work after executing a_func()") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): """Hey yo! Decorate me!""" print("I am the function which needs some decoration to " "remove my foul smell") print(a_function_requiring_decoration.__name__) # Output: a_function_requiring_decoration
and setter usage
class Timer: def __init__(self, value = 0.0): self._time = value self._unit = 's' # Care needs to be taken when using decorators: # 1. Decorator names, function names need to always be # 2. property needs to be declared first, then the setter needs to be written, not the other way around. @property def time(self): return str(self._time) + ' ' + self._unit @ def time(self, value): if(value < 0): raise ValueError('Time cannot be negetive.') self._time = value t = Timer() = 1.0 print()
to this article on the python decorator property and setter usage of the article is introduced to this, more related python property and setter content please search for my previous articles or continue to browse the following related articles I hope you will support me in the future more!