Py in 5: Decorators

kaelscion

kaelscion

Posted on November 15, 2018

Py in 5: Decorators

py-in-5-decorators-cover

originally published on the Coding Duck blog: www.ccstechme.com/coding-duck-blog

Hello everybody and welcome to another Py in 5 discussion. The article series where I take a Python programming concept and try to explain it in human terms in a 5 minute read or less. Our topic today: Decorators!

As is our custom, let's get the official definition of decorators:

pythontips.com:

Functions which modify the functionality of other functions

python.org:

A function returning another function applied as a function transformation using the @wrapper syntax

Okay, so what I'm hearing is....functions. Functions that take a function to return a function so that your app functions functionally. And the programming paradigm used? Object Oriented...obviously.

Even though these definitions aren't at all talking in circles or using their own definition to define themselves, I'm going to try to clarify things anyway.

In a really abstracted sense, think of decorators as a kind of encapsulation, but on the function level rather than the class level. Just like classes in Python allow for encapsulation of their properties, decorators allow this OOP concept within functions. Kind of like when you import a class into your python file, let's say the "time" module and instantiate (or create an instance of) the time class. And let's say you wanted to tell your program to wait 10 seconds before continuing. For this, you would write time.sleep(10). What you are basically doing here is leveraging encapsulation. The "sleep" function is encapsulated (or enclosed) in the time class.

Think of it as if you had a friend name Sleep you wanted to play with as a kid. You would go to his house, ring the bell, and his dad Time would answer the door. "Hi! Can Sleep come out and play?" you would ask Time.

"Sure!" he would respond, "Let me go get him for you." Then, Sleep would come out and you would go have a jolly old time for yourselves. A decorator provides a similar functionality, but rather than encapsulating functions, it encapsulates context. Any function that is called with a decorator, gets access to the context (stuff that happens before and after the function runs that affect the function as it runs) that is unique to that decorator. Kind of like your parents (the class) would have a particular way you needed to behave in a general sense. But when your uncle (the decorator) would take you to do stuff, your behavior would be different than it was at home. What was expected of you, and therefore how you acted, was different while you were under the temporary care of a different relative than your parents.

For example, lets take a look at a few decorators that change the behavior of an html form. The form is written as follows and rendered in the Flask web framework:


@app.route('/')
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''
Enter fullscreen mode Exit fullscreen mode

and renders like this

base-rendering-image-no-decorators

Now, let's add the following decorators to the mix:


def style_with_flex_end(func):
    def func_wrapper():
        return '''<div style="display: flex; flex-direction: row; justify-content: flex-end">{0}</div>'''.format(func())
    return func_wrapper

def style_with_flex_center(func):
    def func_wrapper():
        return '''<div style="display: flex; flex-direction: row; justify-content: center">{0}</div>'''.format(func())
    return func_wrapper

Enter fullscreen mode Exit fullscreen mode

Both decorators do similar things. The only difference between them is the justify-content style tag. However, by simply giving the aforementioned html form the context these decorators apply, we get a different result. Lets attach the style_with_flex_end decorator to this Flask route simply by calling it with an "@" symbol and no parentheses above the definition of the "styling" function.


@app.route('/')
@style_with_flex_end
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''

Enter fullscreen mode Exit fullscreen mode

Adding the decorator renders that same form like this:

rendering-form-with-flex-end-decorator

As you can see, the justify-content: flex-end; context provided by the decorator changed where the form appeared on the page without need to modify the form code itself. Same thing can be done with the our other decorator:


@app.route('/')
@style_with_flex_center
def styling():
    return '''
            <form action='' method='post'>
                <dl>
                  <dt>Username:
                  <dd><input type=text name=username>
                  <dt>Password:
                  <dd><input type=text name=password>
                  <dd><input type=submit value=Login>
                </dl>
            </form>'''

Enter fullscreen mode Exit fullscreen mode

which then renders as follows:

form-rendering-flex-center-decorator

So, in summary, a decorator adds context to a function. It, basically, gives your function "permission" to use the added window dressing that the decorator provides. Our example shows that if we ask the @style_with... decorators if the justify-content style they implement can "come out to play", they will give our html a different set of expectations than when they are "at home with mom and dad", therefore allowing them to behave differently than they normally would.

For a deeper dive into Decorators, check out this SO post that was brought to my attention by @rrampage (thanks so much for sharing, Raunak) which can be found here

I hope this has cleared some things up for those of you who were unsure of what a decorator was or what it did. Please leave a comment below if you have any questions or need clarification! I am always willing to help others understand because the world needs you, your ideas, and your unique approach to this wonderful field so that we can all build the cool, useful, awesome tech of tomorrow!

💖 💪 🙅 🚩
kaelscion
kaelscion

Posted on November 15, 2018

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Py in 5: Decorators
python Py in 5: Decorators

November 15, 2018

Py in 5: Lambda Functions
python Py in 5: Lambda Functions

October 25, 2018

Py in 5: List Comprehensions
python Py in 5: List Comprehensions

October 26, 2018