Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

.. _decorators:

👩‍💻 Function Wrapping and Decorators
=========================================
Function Wrapping and Decorators
=======================================

This section introduces an advanced python concept called *function wrapping* and a special syntax for it called *decorators*. It is not necessary to use decorators in your programming, but they are an elegant way to do function wrapping, and it will be helpful for you to understand what they do when you see them in other people's code.

In Python, functions are "first class" objects, meaning they can be treated like any other object. Beyond calling functions, we can also reference them, pass them as arguments to other functions, or return them. Although we cannot directly manipulate the *behavior* of a function, we can wrap it in another function that does something before or after the original function is called or change the arguments that a function takes. This is called *function wrapping*.

Expand Down Expand Up @@ -84,7 +86,10 @@ We can now easily "enable" or "disable" logging by commenting out the ``@addLogg

To give another example, suppose we wanted to "password protect" access to calling a function. We could create a function ``passwordProtect`` that will wrap our function inside of code that ensures the user has the correct password.

Try running the code below and entering the correct password (``password123``) when prompted. Then, try running the code again and entering an incorrect password. Notice that the ``printSecretMessage`` function is only called if the user enters the correct password.

.. activecode:: ac15_6_4

# This is a decorator function that takes another function as an argument.
def passwordProtect(func):

Expand Down Expand Up @@ -114,4 +119,4 @@ To give another example, suppose we wanted to "password protect" access to calli
# By adding the decorator, we prompt the user for a password before printing the secret message.
printSecretMessage()

Although this example is made up for illustration, this kind of function wrapping can be used in web applications protect access to sensitive pages. For example, code for a Web server might wrap code that transmits personal information with a decorator that checks if the user is logged in. Decorators give us a convenient syntax for modifying the behavior of functions we write.
Although this example is made up for illustration, this kind of function wrapping can be used in web applications to protect access to sensitive pages. For example, code for a Web server might wrap code that transmits personal information with a decorator that checks if the user is logged in. Decorators give us a convenient syntax for modifying the behavior of functions we write.
2 changes: 1 addition & 1 deletion _sources/AdvancedFunctions/toctree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ Advanced Functions
Anonymousfunctionswithlambdaexpressions.rst
ProgrammingWithStyle.rst
MethodInvocations.rst
WPFunctionWrappingAndDecorators.rst
FunctionWrappingAndDecorators.rst
Exercises.rst
ChapterAssessment.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
👩‍💻 Class Decorators
Class Decorators
========================

Recall that Python has a :ref:`"decorator" syntax"<decorators>` that allows us to modify the behavior of functions. We can use this same syntax to modify the behavior of classes. There are two ways we can use decorators with classes: (1) by decorating individual class methods or (2) by decorating the class itself.

**Decorating class methods** is analogous to the function decorators we've already seen. For example, suppose we have the ``addLogging`` from :ref:`earlier <decorators>`::
**Decorating class methods** is analogous to the function decorators we've already seen. For example, suppose we have the ``addLogging`` function from :ref:`earlier <decorators>`::

def addLogging(func): # The argument, func is a function

Expand All @@ -15,16 +15,16 @@ Recall that Python has a :ref:`"decorator" syntax"<decorators>` that allows us t

return wrapper # return our new function

We first need to modify this method slightly to add ``self`` as the first argument. Then, we can use it to decorate any class method that accepts one argument:
We first need to modify this function slightly to add ``self`` as the first argument, since it will be a method of a class. Then, we can use the function to decorate any class method that accepts one argument:

.. activecode:: ac20_15_1

def addLogging(func): # The argument, func is a function
def addLogging(func): # The argument, func is a method of a class

def wrapper(self, x): # x is the argument that we're going to pass to func
print(f"About to call the function with argument {x}")
result = func(self, x) # actually call our function and store the result
print(f"Done with the function with argument {x}. Result: {result}")
print(f"About to call the method with argument {x}")
result = func(self, x) # actually call the method and store the result
print(f"Done with the method invocation with argument {x} on instance {self}. Result: {result}")
return result # return whatever our function returned

return wrapper # return our new function
Expand All @@ -44,6 +44,9 @@ We first need to modify this method slightly to add ``self`` as the first argume
@addLogging
def rePaint(self, color):
self.color = color

def __str__(self):
return(f"***{self.color} {self.make} {self.model} with {self.mileage} miles***")

corvette = Car("Chevrolet", "Corvette", "red", 0)

Expand All @@ -53,7 +56,7 @@ We first need to modify this method slightly to add ``self`` as the first argume
print("-"*20)
corvette.drive(6)

Beyond decorating class methods, we can also **decorate the class itself**. Just like functions in Python, classes are "first class", meaning they can be referenced like any other object, passed as arguments, returned, and wrapped. We decorate classes in almost the same way that we decorate functions, except that our decorator accepts a *class* as an argument, rather than a function. We could then modify the class, or return a new class. For example, suppose we want to create a decorator that adds an extra method (named ``beep``) to a class. We could do that as follows:
Beyond decorating class methods, we can also **decorate the class itself**. Just like functions in Python, classes are "first class", meaning they can be referenced like any other object, passed as arguments, returned, and wrapped. We decorate classes in almost the same way that we decorate functions, except that our decorator accepts a *class* as an argument, rather than a function. We could then modify the class, or return a new class. For example, suppose we want to create a decorator (named ``addBeep``) that adds an extra method (named ``beep``) to any class. We could do that as follows:

.. activecode:: ac20_15_2

Expand Down
2 changes: 1 addition & 1 deletion _sources/Classes/toctree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Defining your own Classes
ThinkingAboutClasses.rst
TestingClasses.rst
Tamagotchi.rst
WPClassDecorators.rst
ClassDecorators.rst
Glossary.rst
Exercises.rst
ChapterAssessment.rst