SimleNamespace
is a Python utility that allow you to create simple Python objects.
With it, you can turn a dictionary into an object where the keys are accessible using the “dot notation”.
python> from types import SimpleNamespace
python> person_dict = {
"firstname": "John",
"lastname": "Doe",
"age": 29
}
python> person = SimpleNamespace(**person_dict)
python> person.firstname
'John'
python> person_dict.firstname
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'firstname'
You can see it as a simple data structure. You can use it instead of a Class when you need an object that do need to implement any kind of behavior.
SimpleNamespace vs. Dataclass vs. namedtuple
Here is a recap of the main strength for each types:
SimpleNamespace | Dataclass | namedtuple | classes |
---|---|---|---|
prototyping & dot notation | post_init | immutability | behaviors |
Use-Case: Mocking & Prototyping
You have an API endpoint you want to query.
This endpoint returns JSON data.
You have a python method taking this JSON data as an input, transforms it and returns the transformed JSON as output.
Note: this is typically the case when you have e.g. a Google Cloud Function, listening to the outbound webhook where data are delivered by one external service and passing on the data to another external service after having performed some transformation over it. Usually, extracting data from the received HTTP payload and preparing a JSON card to be send to another endpoint via an HTTP POST request. Could be alerts you want to send to Microsoft Teams.
Here is the snippet doing the aforementioned actions:
from typing import Any
import requests
from pprint import pprint
from types import SimpleNamespace
def request_random_user() -> requests.Response:
"""
Method fetching one random user from the API.
"""
response = requests.get(
url="https://random-data-api.com/api/v2/users?size=1",
timeout=300
)
response.raise_for_status()
return response
def reduce_json_content(
response: requests.Response
) -> dict[str, Any]:
"""
Method returning a dict with selected
subfields from the json content in the
HTTP response.
"""
content = response.json()
selected_fields = ("first_name", "last_name", "username")
return {field: content[field] for field in selected_fields}
def main() -> None:
response = request_random_user()
content = reduce_json_content(response)
pprint(content)
if __name__ == "__main__":
main()
Let’s give it a shot:
zsh> poetry run python main.py
{
'first_name': 'Eula',
'last_name': 'Morar',
'username': 'eula.morar'
}
Now, let’s say you want to test the reduce_json_content
method without calling the API.
You can simply pass on to the method a mock json content.
SimpleNamespace
is the perfect too for that.
Let’s edit the above snippet, replacing the last lines by the following:
if __name__ == "__main__":
response_mock = SimpleNamespace()
response_mock.json = lambda: {
"first_name": "John",
"last_name": "Doe",
"username": "johndoe",
}
content = reduce_json_content(response_mock)
pprint(content)
Note: response is of type requests.Response
. It possess a .json()
method. Therefore, we need to mock this method using a lambda
function. This lambda function does not require any parameters.
zsh> poetry run python main.py
{'first_name': 'John', 'last_name': 'Doe', 'username': 'johndoe'}
Voilà! You can then continue the development without calling the API every time.