Introduction
Python has a few interesting ways to call functions. This guide will show a few interesting ways to do so that's outside of a standard function call. These techniques can be used for more advanced cases, especially dynamic calling.
Partials
Partials are essentially templates for how a function is called. Take for example a common json dumps I use to give indented JSON output:
from json import dumps
test_dictionary = {'a': 'foo', 'b': 'bar'}
print(dumps(test_dictionary, indent=2, default=str))
Now the thing is I'm always going to be calling it this way. It indents by 2 spaces making it easier to read, and default=str
defaults to using the str()
construct when it can't parse normally (this happens frequently for some timestamp related values). Calling it this way is a bit verbose, so we can use a partial instead:
from functools import partial
from json import dumps
test_dictionary = {'a': 'foo', 'b': 'bar'}
indented_json = partial(dumps, indent=2, default=str)
print(indented_json(test_dictionary))
This is nice because it prevents the need to write function just for the purpose of calling other functions, especially if there's a lot of arguments going with it. Note that any required arguments not present in the partial will need to be filled in during the call.
Dynamic Keyword Args
Python has an interesting mechanism for passing in keyword args as a dictionary of name-> value pairs:
from json import dumps
values = {'a': 1, 'b': 2, 'c': 3}
keyword_args = {
'indent': 2,
'default': str
}
print(dumps(values, **keyword_args))
The **
operator is often referred to as splat, or unpacking as well. This is the equivalent call wise to:
dumps(values, indent=2, default=str)
This can be useful in some boto calls where you're building up values to pass on. DynamoDB in particular can utilize this to build up filters and query expressions.
Function Mapping
In cases of calling a function via a string, a mapping using something like a dictionary makes this possible:
def double_values(value):
return value**2
function_mapping = {
'double': double_values
}
print(function_mapping['double'](2))
I prefer this to eval()
usage as it essentially lets me create an approved list of functions to call.
Dynamic Method Calling
Now methods are essentially attributes of a class or class instance. For static methods bound to the class itself (not reliant on self
variables), getattr
can simply be used with the class and method name:
class Test:
@staticmethod
def hello_world():
return "Hello, World"
hello_method = getattr(Test, 'hello_world')
print(hello_method())
Now if the class name is also dynamic, you need to either access it via globals()
like so:
class Test:
@staticmethod
def hello_world():
return "Hello, World"
hello_method = getattr(globals()['Test'], 'hello_world')
print(hello_method())
or if it's part of the module then getattr
can be used on the namespace declaration to obtain it:
import csv
writer_class = getattr(csv, 'DictWriter')
This could then be passed to getattr
again to obtain method in DictWriter
. For this to work on class instances (ie. keeping self type values isolated) then getattr
will need to be done on the class instance assignment itself:
class Math:
def __init__(self, value=2):
self.value = value
def double(self):
return self.value ** 2
math_instance = Math()
math_instance2 = Math(4)
double_method = getattr(math_instance, 'double')
double_method2 = getattr(math_instance2, 'double')
print(double_method())
print(double_method2())
Note that if you're using this for something like dynamic plugin calls consider something like Abstract Base Class to to ensure a consistent method name exists to be executed (run_plugin
for example).
Conclusion
That's it for this lesson on advanced ways to do function (and method) calls in Python. If you like what you see I'm available for hire.
Top comments (0)