How to call Python functions dynamically

Question:

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?

Asked By: nemesisdesign

||

Answers:

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()
Answered By: Magnus Hoff
for field in fields:
    vars()['clean_' + field]()
Answered By: Matt Joiner

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.

Answered By: Jakob Bowyer

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.

Answered By: Alexander Solovyov

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)()
Answered By: ceth

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.

Answered By: Spacedman

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.

Answered By: martineau

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)()
Answered By: khachik

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.

Answered By: TheHowlingHoaschd

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
Answered By: Vlad Bezden

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)
Answered By: Muhammad Shahbaz
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.