How to call Python functions dynamically

PythonFunctional Programming

Python Problem Overview


I have this code:

fields = ['name','email']

def clean_name():
    pass

def clean_email():
    pass

How can I call clean_name() and clean_email() dynamically?

For example:

for field in fields:
    clean_{field}()

I used the curly brackets because it's how I used to do it in PHP but obviously doesn't work.

How to do this with Python?

Python Solutions


Solution 1 - Python

If don't want to use globals, vars and don't want make a separate module and/or class to encapsulate functions you want to call dynamically, you can call them as the attributes of the current module:

import sys
...
getattr(sys.modules[__name__], "clean_%s" % fieldname)()

Solution 2 - Python

Using global is a very, very, bad way of doing this. You should be doing it this way:

fields = {'name':clean_name,'email':clean_email}

for key in fields:
    fields[key]()

Map your functions to values in a dictionary.

Also using vars()[] is wrong too.

Solution 3 - Python

It would be better to have a dictionary of such functions than to look in globals().

The usual approach is to write a class with such functions:

class Cleaner(object):
    def clean_name(self):
        pass

and then use getattr to get access to them:

cleaner = Cleaner()
for f in fields:
    getattr(cleaner, 'clean_%s' % f)()

You could even move further and do something like this:

class Cleaner(object):
    def __init__(self, fields):
        self.fields = fields

    def clean(self):
        for f in self.fields:
            getattr(self, 'clean_%s' % f)()

Then inherit it and declare your clean_<name> methods on an inherited class:

cleaner = Cleaner(['one', 'two'])
cleaner.clean()

Actually this can be extended even further to make it more clean. The first step probably will be adding a check with hasattr() if such method exists in your class.

Solution 4 - Python

I have come across this problem twice now, and finally came up with a safe and not ugly solution (in my humble opinion).

RECAP of previous answers:

globals is the hacky, fast & easy method, but you have to be super consistent with your function names, and it can break at runtime if variables get overwritten. Also it's un-pythonic, unsafe, unethical, yadda yadda...

Dictionaries (i.e. string-to-function maps) are safer and easy to use... but it annoys me to no end, that i have to spread dictionary assignments across my file, that are easy to lose track of.

Decorators made the dictionary solution come together for me. Decorators are a pretty way to attach side-effects & transformations to a function definition.

Example time

fields = ['name', 'email', 'address']

# set up our function dictionary
cleaners = {}

# this is a parametered decorator
def add_cleaner(key):
    # this is the actual decorator
    def _add_cleaner(func):
        cleaners[key] = func
        return func
    return _add_cleaner

Whenever you define a cleaner function, add this to the declaration:

@add_cleaner('email')
def email_cleaner(email):
    #do stuff here
    return result
    

The functions are added to the dictionary as soon as their definition is parsed and can be called like this:

cleaned_email = cleaners['email'](some_email)

Alternative proposed by PeterSchorn:

def add_cleaner(func):
    cleaners[func.__name__] = func
    return func

@add_cleaner
def email():
   #clean email

This uses the function name of the cleaner method as its dictionary key. It is more concise, though I think the method names become a little awkward. Pick your favorite.

Solution 5 - Python

globals() will give you a dict of the global namespace. From this you can get the function you want:

f = globals()["clean_%s" % field]

Then call it:

f()

Solution 6 - Python

Here's another way:

myscript.py:

def f1():
    print 'f1'

def f2():
    print 'f2'

def f3():
    print 'f3'

test.py:

import myscript

for i in range(1, 4):
    getattr(myscript, 'f%d' % i)()

Solution 7 - Python

I had a requirement to call different methods of a class in a method of itself on the basis of list of method names passed as input (for running periodic tasks in FastAPI). For executing methods of Python classes, I have expanded the answer provided by @khachik. Here is how you can achieve it from inside or outside of the class:

>>> class Math:
...   def add(self, x, y):
...     return x+y
...   def test_add(self):
...     print(getattr(self, "add")(2,3))
... 
>>> m = Math()
>>> m.test_add()
5
>>> getattr(m, "add")(2,3)
5

Closely see how you can do it from within the class using self like this:

getattr(self, "add")(2,3)

And from outside the class using an object of the class like this:

m = Math()
getattr(m, "add")(2,3)

Solution 8 - Python

Here's another way: define the functions then define a dict with the names as keys:

>>> z=[clean_email, clean_name]
>>> z={"email": clean_email, "name":clean_name}
>>> z['email']()
>>> z['name']()

then you loop over the names as keys.

or how about this one? Construct a string and use 'eval':

>>> field = "email"
>>> f="clean_"+field+"()"
>>> eval(f)

then just loop and construct the strings for eval.

Note that any method that requires constructing a string for evaluation is regarded as kludgy.

Solution 9 - Python

for field in fields:
    vars()['clean_' + field]()

Solution 10 - Python

In case if you have a lot of functions and a different number of parameters.

class Cleaner:
    @classmethod
    def clean(cls, type, *args, **kwargs):
        getattr(cls, f"_clean_{type}")(*args, **kwargs)

    @classmethod
    def _clean_email(cls, *args, **kwargs):
        print("invoked _clean_email function")

    @classmethod
    def _clean_name(cls, *args, **kwargs):
        print("invoked _clean_name function")


for type in ["email", "name"]:
    Cleaner.clean(type)

Output:

invoked _clean_email function
invoked _clean_name function

Solution 11 - Python

I would use a dictionary which mapped field names to cleaning functions. If some fields don't have corresponding cleaning function, the for loop handling them can be kept simple by providing some sort of default function for those cases. Here's what I mean:

fields = ['name', 'email', 'subject']

def clean_name():
    pass
def clean_email():
    pass

# (one-time) field to cleaning-function map construction
def get_clean_func(field):
    try:
        return eval('clean_'+field)
    except NameError:
        return lambda: None  # do nothing
clean = dict((field, get_clean_func(field)) for field in fields)

# sample usage
for field in fields:
    clean[field]()

The code above constructs the function dictionary dynamically by determining if a corresponding function named clean_<field> exists for each one named in the fields list. You likely would only have to execute it once since it would remain the same as long as the field list or available cleaning functions aren't changed.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnemesisdesignView Question on Stackoverflow
Solution 1 - PythonkhachikView Answer on Stackoverflow
Solution 2 - PythonJakob BowyerView Answer on Stackoverflow
Solution 3 - PythonAlexander SolovyovView Answer on Stackoverflow
Solution 4 - PythonTheHowlingHoaschdView Answer on Stackoverflow
Solution 5 - PythonMagnus HoffView Answer on Stackoverflow
Solution 6 - PythoncethView Answer on Stackoverflow
Solution 7 - PythonMuhammad ShahbazView Answer on Stackoverflow
Solution 8 - PythonSpacedmanView Answer on Stackoverflow
Solution 9 - PythonMatt JoinerView Answer on Stackoverflow
Solution 10 - PythonVlad BezdenView Answer on Stackoverflow
Solution 11 - PythonmartineauView Answer on Stackoverflow