Python’s functional aspects
Python [is] multi-paradigm; you can write programs or libraries that are largely procedural, object-oriented, or functional.
Simplicity, ease-of-use and flexibility are hallmarks of the Python programming language. Its functional constructs, such as iterators and generators, list comprehension, built-in functions like
functools, are among its most useful features.
While this post is only about
functools.partial(), I encourage you to read through the Functional Programming HOWTO document, as it contains a treasure trove of highly useful tools that Python provides.
The function of
Consider a Python function
f(a, b, c); you may wish to create a new function
g(b, c)that’s equivalent to
f(1, b, c); you’re filling in a value for one of
f()’s parameters. This is called “partial function application”.
And this is exactly what
functools.partial() does. It takes a function as its first argument, and then any number of positional and keyword arguments which you can use to configure the partial function application.
Let’s take the explanation above, of
f(a, b, c), and put it in terms of Python code.
functools.partial(), let’s construct a function,
g(b, c), that essentially calls
f(1, b, c):
That’s it! Now, for example, calling
g(3, 2) is equivalent to calling
f(1, 3, 2):
>>> import functools >>> def f(a, b, c): ... print('a is', a) ... print('b is', b) ... print('c is', c) ... return (a + b) * c ... >>> g = functools.partial(f, 1) >>> g(3, 2) a is 1 b is 3 c is 2 8 >>> f(1, 3, 2) a is 1 b is 3 c is 2 8
What if you wanted to set the value of
a as well as
What if you wanted to set the value of
c alone? Two options come readily to mind. Either:
cto the first positional argument:
- Or, make
ca keyword argument:
Application: single-reply web server
This seems weird; a web server that spins up, replies to a single request, and shuts down?
That’s right. It’s a contrived scenario, too, so bear with me while we glance over:
- I wanted to build a CLI application that would make use of an OAuth2-enabled API.
- The Authorization Server in question did not support what would’ve been the appropriate mechanism: device flow.
- Now I’m forced to use the authorization code flow, which is meant for actual web servers with multiple users.
- Since this is a CLI program with just me using it, I only need to listen for the authorization code delivery one time (see where this is going?). I’m referring to step 5 in this diagram.
In order to handle the authorization code delivery (it will come to me as a
GET request from the Authorization Server), I subclassed
BaseHTTPRequestHandler as such:
from http import HTTPStatus from http.server import BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs class CallbackHandler(BaseHTTPRequestHandler): def __init__(self, result, *args, **kwargs): # This dictionary will be used to extract the authorization code # from the request parameters (query string). self.result = result if type(result) == dict else dict() super().__init__(*args, **kwargs) def do_GET(self): self.handle_callback() self.send_response_only(HTTPStatus.ACCEPTED) self.end_headers() self.wfile.write(HTTPStatus.ACCEPTED.description.encode()) def handle_callback(self): query = urlparse(self.path).query self.result.update(parse_qs(query))
Firstly, because that’s how you use
BaseHTTPRequestHandler. By itself, it doesn’t know how to handle requests. You must implement your own
do_*() methods for each HTTP method you need to support. In my case, I just need
Secondly, because I need to extract information from the request parameters; however, I won’t be able to use the class instance to do that, and so I’ve added a positional argument,
result, which is a dictionary because if I pass in my own dictionary, its values will be updated in-place, effectively extracting the information I need into a variable that the program can use to finish the OAuth2 transaction.
In case you missed it above, the
handle_callback() function that I defined in
CallbackHandler is what updates the dictionary with the contents of the request’s query string:
Why not just access class variables?
HTTPServer is built with a handler class (not instance). For example:
As you can see, I don’t instantiate the handler class myself; furthermore, after the handler class gets instantiated during the construction of
HTTPServer instance doesn’t expose easy access to its own instance of that handler class.
So, by passing in my own dictionary into my handler class,
CallbackHandler, I can update that dictionary with the necessary information and later access it without directly accessing the
I just said I won’t be instantiating the handler class myself, right? So how can I pass any arguments to
Turns out that
functools.partial accepts not just functions, but callables! I can give it my handler class with predefined arguments!
Let’s see it at work:
my_result will contain the names and values of the request’s query string parameters!
At this point in the OAuth2 authorization code flow, the query string parameters will contain, among other things, the authorization code needed to obtain, from the Authorization Server, an access token with which to finally contact the API.
Moral of the story: please add device flow support to your Authorization Servers. =)