In Python, what's the correct way to instantiate a class from a variable?
Categories:
Dynamic Class Instantiation in Python: A Comprehensive Guide

Learn the correct and safe methods to instantiate a Python class when its name is stored in a variable, covering best practices and potential pitfalls.
In Python, you often encounter situations where you need to create an instance of a class, but the class itself is not directly referenced in your code. Instead, its name might be stored in a string variable, perhaps read from a configuration file, a database, or user input. This article explores the various techniques for dynamically instantiating classes from a variable, discussing their use cases, advantages, and security considerations.
The globals()
and locals()
Approach
Python provides built-in functions globals()
and locals()
that return dictionaries representing the current global and local symbol tables, respectively. These dictionaries map names to their corresponding objects. If your class is defined in the current scope, you can retrieve it using its name from these dictionaries.
class MyClass:
def __init__(self, value):
self.value = value
def greet(self):
return f"Hello from MyClass with value {self.value}"
class AnotherClass:
def __init__(self, name):
self.name = name
def identify(self):
return f"I am AnotherClass, named {self.name}"
class_name_str = "MyClass"
# Using globals()
if class_name_str in globals():
MyClassRef = globals()[class_name_str]
instance = MyClassRef(123)
print(instance.greet())
class_name_str_2 = "AnotherClass"
# Using locals() (if defined in local scope, e.g., inside a function)
def create_instance_locally(cls_name_str, *args, **kwargs):
if cls_name_str in locals():
ClassRef = locals()[cls_name_str]
return ClassRef(*args, **kwargs)
return None
# For this example, AnotherClass is global, so locals() won't find it directly here
# but if it were defined inside create_instance_locally, this would work.
# For demonstration, we'll use globals() again.
if class_name_str_2 in globals():
AnotherClassRef = globals()[class_name_str_2]
instance_2 = AnotherClassRef("Dynamic")
print(instance_2.identify())
Instantiating classes using globals()
and locals()
.
globals()
and locals()
can work, they are generally less preferred for dynamic class loading, especially if the class might be in a different module. They are more suitable for introspecting the current execution environment.The getattr()
Approach for Module-Level Classes
A more robust and common method for instantiating classes from a variable, especially when they reside within a module (either the current one or an imported one), is to use the getattr()
function. This function allows you to retrieve an attribute (which can be a class) from an object (which can be a module) by its string name.
# Assume MyClass and AnotherClass are defined in the current module (e.g., main.py)
import sys
class MyClass:
def __init__(self, value):
self.value = value
def greet(self):
return f"Hello from MyClass with value {self.value}"
class AnotherClass:
def __init__(self, name):
self.name = name
def identify(self):
return f"I am AnotherClass, named {self.name}"
def instantiate_from_module(module_name, class_name_str, *args, **kwargs):
try:
module = sys.modules[module_name] # Get the module object
if hasattr(module, class_name_str):
ClassRef = getattr(module, class_name_str)
if isinstance(ClassRef, type): # Ensure it's actually a class
return ClassRef(*args, **kwargs)
else:
print(f"'{class_name_str}' in module '{module_name}' is not a class.")
else:
print(f"Class '{class_name_str}' not found in module '{module_name}'.")
except KeyError:
print(f"Module '{module_name}' not found.")
return None
# Example usage:
instance_1 = instantiate_from_module(__name__, "MyClass", 456)
if instance_1:
print(instance_1.greet())
instance_2 = instantiate_from_module(__name__, "AnotherClass", "Module-Loaded")
if instance_2:
print(instance_2.identify())
# Example of a non-existent class
non_existent = instantiate_from_module(__name__, "NonExistentClass")
Using getattr()
with sys.modules
for dynamic instantiation.
flowchart TD A[Start: Class Name as String] --> B{Determine Module Source?} B -- Yes, current module --> C1[Get current module object (e.g., `sys.modules[__name__]`)] B -- Yes, specific module --> C2[Import module (e.g., `importlib.import_module`)] B -- No, global/local scope --> C3[Check `globals()` or `locals()`] C1 --> D{Retrieve Class using `getattr(module, class_name)`} C2 --> D C3 --> D_alt{Retrieve Class using `dict[class_name]`} D --> E{Is retrieved object a class?} D_alt --> E E -- Yes --> F[Instantiate Class: `ClassRef(*args, **kwargs)`] E -- No --> G[Error: Not a class or not found] F --> H[Success: Class Instance] G --> I[Failure: Handle Error] H --> J[End] I --> J
Flowchart for dynamic class instantiation strategies.
The importlib
Module: The Recommended Approach
For truly dynamic loading of classes from arbitrary modules, the importlib
module is the most Pythonic and recommended solution. It provides an API for programmatically importing modules, which is crucial when the module name itself is also dynamic. This approach is cleaner and more explicit than directly manipulating sys.modules
or relying on __import__
.
# Assume you have a file named 'my_module.py' with the following content:
#
# # my_module.py
# class ExternalClass:
# def __init__(self, data):
# self.data = data
#
# def get_data(self):
# return f"Data from ExternalClass: {self.data}"
#
# class AnotherExternalClass:
# def __init__(self, id):
# self.id = id
#
# def get_id(self):
# return f"ID from AnotherExternalClass: {self.id}"
import importlib
def instantiate_from_path(module_path, class_name_str, *args, **kwargs):
try:
module = importlib.import_module(module_path)
if hasattr(module, class_name_str):
ClassRef = getattr(module, class_name_str)
if isinstance(ClassRef, type):
return ClassRef(*args, **kwargs)
else:
print(f"'{class_name_str}' in module '{module_path}' is not a class.")
else:
print(f"Class '{class_name_str}' not found in module '{module_path}'.")
except ImportError:
print(f"Module '{module_path}' could not be imported.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
return None
# Example usage:
# Make sure 'my_module.py' is in your Python path or current directory
instance_ext_1 = instantiate_from_path("my_module", "ExternalClass", "hello")
if instance_ext_1:
print(instance_ext_1.get_data())
instance_ext_2 = instantiate_from_path("my_module", "AnotherExternalClass", id=101)
if instance_ext_2:
print(instance_ext_2.get_id())
# Example with a non-existent module or class
non_existent_module = instantiate_from_path("non_existent_module", "SomeClass")
non_existent_class = instantiate_from_path("my_module", "UnknownClass")
Using importlib.import_module()
for dynamic class instantiation from a specified module.
Security Considerations and Best Practices
Dynamic class instantiation, while powerful, carries inherent risks if not handled carefully. The primary concern is security, especially when the class name or module path comes from external, untrusted input. Always prioritize safety and maintainability.
1. Whitelist Allowed Classes
Instead of allowing any string to be instantiated, maintain a whitelist (a dictionary or set) of allowed class names and their corresponding class objects. This provides a controlled environment.
2. Validate Inputs Rigorously
If class names or module paths are user-provided, ensure they conform to expected patterns (e.g., valid Python identifiers, no path traversal attempts). Regular expressions can be useful here.
3. Use importlib
Over __import__
or eval()
importlib
is the official and safest way to programmatically import modules. Avoid __import__
directly, and absolutely avoid eval()
for dynamic class loading, as eval()
can execute arbitrary code.
4. Handle Exceptions Gracefully
Wrap your dynamic loading logic in try-except
blocks to catch ImportError
, AttributeError
, TypeError
, and other potential issues, providing informative error messages.
5. Limit Scope and Permissions
If possible, run code that performs dynamic loading in an environment with limited permissions, especially if dealing with potentially untrusted inputs.
By following these guidelines, you can leverage the flexibility of dynamic class instantiation in Python while mitigating the associated risks, leading to more robust and secure applications.