Keywords: Python | dynamic calling | function | string | getattr
Abstract: This article explores methods to call functions or methods dynamically based on string names in Python. It covers using getattr for class methods, globals() and locals() for functions, dictionary mapping as an alternative, and warns against using eval() due to security risks. Best practices are recommended for safe and efficient code.
Introduction
In Python programming, there are scenarios where you need to call a function or method dynamically based on a string representation of its name. This is common in cases like command-line interfaces, plugin systems, or configuration-driven code. The question is: how can you safely and efficiently invoke a function when its name is stored as a string?
Using getattr for Dynamic Method Invocation in Classes
For methods within a class, Python provides the getattr function to retrieve an attribute by name. Here is an example based on the best answer:
class MyClass(object):
def install(self):
print "In install"
method_name = 'install'
my_cls = MyClass()
try:
method = getattr(my_cls, method_name)
method()
except AttributeError:
raise NotImplementedError("Class does not implement the method")
This approach uses exception handling to manage cases where the method does not exist, ensuring robustness.
Dynamic Function Calling with globals() and locals()
For standalone functions, you can use the globals() and locals() dictionaries to access the function by name. The following code demonstrates this:
def install():
print "In install"
method_name = 'install'
possibles = globals().copy()
possibles.update(locals())
method = possibles.get(method_name)
if not method:
raise NotImplementedError("Method not implemented")
method()
By combining global and local namespaces, this method allows for flexible function lookup.
Dictionary-Based Function Mapping
An alternative approach, as suggested in another answer, is to use a dictionary to map string names to functions. This provides explicit control and can be more secure:
def install():
print "In install"
methods = {'install': install}
method_name = 'install'
if method_name in methods:
methods[method_name]()
else:
raise Exception("Method not implemented")
This method is straightforward and avoids the complexities of namespace inspection.
Why eval() Should Be Avoided
While eval() can execute code from a string, it is highly discouraged due to security vulnerabilities. For example:
method_name = 'install()'
eval(method_name) # Not recommended
Using eval() can lead to code injection attacks if the string is derived from untrusted sources. Therefore, it should be avoided in production code.
Best Practices and Recommendations
For class methods, prefer getattr with proper error handling. For functions, using globals() and locals() is effective, but dictionary mapping offers better control and safety. Always validate input strings and avoid eval() to prevent security risks. These techniques enable dynamic behavior while maintaining code integrity.