Question

This is an example for python decorator. I am unable to understand the way it works. Please explain me the given example's control flow. I will be highly obliged.

def helloSolarSystem(original_function):
   def new_function(*args, **kwargs):
        original_function(*args, **kwargs)  
        print("Hello, solar system!")
   return new_function

def helloGalaxy(original_function):
    def new_function(*args, **kwargs):
        original_function(*args, **kwargs)  
        print("Hello, galaxy!")
    return new_function

@helloGalaxy
@helloSolarSystem
def hello(targetName=None):
     if targetName:
        print("Hello, " +  targetName +"!")
     else:
        print("Hello, world!")
hello("Earth")
Was it helpful?

Solution

Decorators are syntactic sugar for applying higher-order functions in Python. A higher-order function is a function that takes one or more functions as inputs and returns a function. i.e.

h(x) = f(g(x))

where here f() is a higher-order function that takes a function of a single argument, g(x), and returns a function of a single argument, h(x). You can think of f() as modifying the behaviour of g().

Higher-order functions are composable (by definition), so in your specific example, the decorator syntax,

@helloGalaxy
@helloSolarSystem
def hello(targetName=None):
    ...

is equivalent to,

hello = helloGalaxy(helloSolarSystem(hello))

By substituting hello into helloSolarSystem, then the result of that into helloGalaxy, we get the equivalent function call,

def hello(targetName=None):
    if targetName:                            |        
        print("Hello, " + targetName + "!")   |  (1)  |
    else:                                     |       |  (2)   |
        print("Hello, world!")                |       |        |  (3)
    print("Hello, solar system!")                     |        |
    print("Hello, galaxy!")                                    |

where (1) is the application of the original hello(), (2) is the application of,

def helloSolarSystem(original_function):
    def new_function(*args, **kwargs):
        original_function(*args, **kwargs)   <-- (1)
        print("Hello, solar system!")
    return new_function

and (3) is the application of,

def helloGalaxy(original_function):
    def new_function(*args, **kwargs):
        original_function(*args, **kwargs)   <-- (2)
        print("Hello, galaxy!")
    return new_function

OTHER TIPS

This is simply the observation that in Python, functions are objects like everything else. Ah, function containing variable, you’re not so special!

>>> issubclass(int, object) # all objects in Python inherit from a common baseclass
True
>>> def foo():
...     pass
>>> foo.__class__ # 1
<type 'function'>
>>> issubclass(foo.__class__, object)
True

In your example, @helloGalaxy implies hello = helloGalaxy(hello)

similarly, @helloSolarSystem implies hello = helloSolarSystem(hello)

when you called

@helloGalaxy

@helloSolarSystem before hello() function

hello will be decorated by helloGalaxy and helloSolarSystem

so it new_function() will be overridden by helloSolarSystem().

Before hello(“Earth”) is called the function hello is decorated by newFunction() of helloSolarSystem() and helloGalaxy() decorators.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top