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:
fromjsonimportdumpstest_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, anddefault=str
defaults to using thestr()
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:
fromfunctoolsimportpartialfromjsonimportdumpstest_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:
fromjsonimportdumpsvalues={'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:
defdouble_values(value):returnvalue**2function_mapping={'double':double_values}print(function_mapping['double'](2))
I prefer this toeval()
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 onself
variables),getattr
can simply be used with the class and method name:
classTest:@staticmethoddefhello_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 viaglobals()
like so:
classTest:@staticmethoddefhello_world():return"Hello, World"hello_method=getattr(globals()['Test'],'hello_world')print(hello_method())
or if it's part of the module thengetattr
can be used on the namespace declaration to obtain it:
importcsvwriter_class=getattr(csv,'DictWriter')
This could then be passed togetattr
again to obtain method inDictWriter
. For this to work on class instances (ie. keeping self type values isolated) thengetattr
will need to be done on the class instance assignment itself:
classMath:def__init__(self,value=2):self.value=valuedefdouble(self):returnself.value**2math_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 likeAbstract 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 seeI'm available for hire.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse