Easily navigate nested data structures for building readable data pipelines.
Do you ever find yourself writing ugly code to access nested values? Fret no longer!
data = {
"users": {
"Alice": {
"profile": {
"age": 30,
"email": "alice@example.com"
}
},
"Bob": {
"profile": {
"age": 25,
"email": "bob@example.com"
}
}
}
}
alice_age = data["users"]["Alice"]["profile"]["age"]
alice_email = data["users"]["Alice"]["profile"]["email"]
bob_age = data.get("users", {}).get("Bob", {}).get("profile", {}).get("age", "Unknown")
bob_email = data.get("users", {}).get("Bob", {}).get("profile", {}).get("email", "Unknown")from datapath import Path
alice_age = Path.users.Alice.profile.age(data)
alice_email = Path.users.Alice.profile.email(data)
bob_age= Path.users.Bob.profile.age(data, default="Unknown")
bob_email = Path.users.Bob.profile.email(data, default="Unknown")- Easy navigation of nested data: Use simple attribute and item access to traverse nested dictionaries and lists.
- Flexible data retrieval: Retrieve data with optional type checking and default values if the path does not exist or leads to an error.
- Enhanced readability: Create more readable code by abstracting complex data access into straightforward, path-based retrievals.
datapath is available on PyPI, so you can install it using using pip:
pip install datapathThe Path class is the core of the datapath package. Here's how you can use it:
from datapath import Path
# Create a Path simply by chaining attributes or items
path = Path.data.users[0].nameOnce you've created a path, you can use it to access data in nested structures:
# Given a nested dictionary
data = {
"data": {
"users": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
]
}
}
# To retrieve data using the path, simply call the path with the data as an argument.
user_name = Path.data.users[0].name(data)
print(user_name) # Prints 'Alice'If a part of the path doesn't exist, you can specify a default value to return instead of raising an error:
# Returns 'Unknown' if the specified index is out of range
name = Path.data.users[2].name(data, default="Unknown")
print(name) # Prints 'Unknown'You can enforce the type of the returned value:
# Specify the expected type, returns the value if it matches, raises a TypeError otherwise
age = Path.data.users[0].age(data, type_=int)
print(age) # Prints 30Note: Type enforcement can be disabled by setting
check_typeto False.type_andoptionalcan still be used to define type hints for your data whencheck_typeis False.
You can specify that a return type is optional, which means it will return None if the path is not found or the type does not match, rather than raising an error:
# Optional type checking, returns None if not found or type mismatch
birthday = Path.data.users[0].birthday(data, optional=str)
print(birthday) # Prints None since 'birthday' does not existThe __call__ method of the Path class supports several arguments that adjust the behavior to be used when accessing values:
- data: The nested structure to be accessed.
- default: If provided, this value is returned when the path leads to an error or does not exist.
- type_: Enforces that the returned value matches this type, raising a TypeError if it does not.
- optional: Like
type_, but returnsNoneinstead of raising a KeyError when the path does not exist. - check_type: If
True, the type of the returned value is checked againsttype_oroptional(default isTrue). If the type does not match, a TypeError is raised. WhenFalse, no type checking is performed (faster but less safe).