A lightweight Python package for accessing dictionary keys and list elements using dot notation with automatic recursive conversion of nested structures.
pip install dotdict3>>> from dotdict3 import DotDict
>>> d = DotDict({"a": "b"})
>>> d.a
'b'
- Dot notation access: Access dictionary keys as attributes (
data.keyinstead ofdata['key']) - Automatic conversion: Nested dictionaries and iterables are automatically converted to
DotDictandDotList - Recursive processing: Works seamlessly with deeply nested structures
- Standard compatibility: Fully compatible with built-in
dictandlistoperations
from dotdict3 import DotDict
data = DotDict({
'name': 'John',
'age': 30,
'address': {
'city': 'New York',
'zipcode': '10001'
}
})
# Access with dot notation
print(data.name) # 'John'
print(data.address.city) # 'New York'
# Still works with bracket notation
print(data['age']) # 30A dictionary subclass that allows attribute-style access to keys.
- Attribute access: Get, set, and delete keys using dot notation
- Empty initialization: Create empty instances with
DotDict()(no arguments needed) - Automatic nesting: Nested dictionaries are automatically converted to
DotDict - Iterable conversion: Lists, tuples, sets, and ranges are converted to
DotList - Dict compatibility: All standard
dictmethods work as expected
from dotdict3 import DotDict
# Empty initialization
empty = DotDict()
print(len(empty)) # 0
# Basic usage
user = DotDict({'name': 'Alice', 'role': 'admin'})
print(user.name) # 'Alice'
# Setting values
user.email = 'alice@example.com'
print(user.email) # 'alice@example.com'
# Nested dictionaries
config = DotDict({
'database': {
'host': 'localhost',
'port': 5432
}
})
print(config.database.host) # 'localhost'
# Deleting keys
del user.roleA list subclass that automatically converts nested dictionaries and iterables.
- Empty initialization: Create empty instances with
DotList()(no arguments needed) - Automatic conversion: Dictionaries in the list become
DotDictinstances - Nested iterables: Lists, tuples, sets, and ranges become
DotListinstances - List compatibility: All standard
listmethods work as expected
from dotdict3 import DotList
# Empty initialization
empty = DotList()
print(len(empty)) # 0
# Basic usage
items = DotList([1, 2, 3])
print(items[0]) # 1
# Lists with dictionaries
users = DotList([
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25}
])
print(users[0].name) # 'Alice'
# Nested lists
matrix = DotList([[1, 2], [3, 4]])
print(matrix[0][1]) # 2
# Appending
users.append({'name': 'Charlie', 'age': 35})
print(users[2].name) # 'Charlie'from dotdict3 import DotDict
data = DotDict({
'users': [
{
'name': 'Alice',
'scores': [95, 87, 92],
'metadata': {
'joined': '2024-01-01',
'tags': ['admin', 'active']
}
},
{
'name': 'Bob',
'scores': [88, 90, 85],
'metadata': {
'joined': '2024-02-15',
'tags': ['user']
}
}
],
'settings': {
'theme': 'dark',
'notifications': True
}
})
# Access deeply nested data
print(data.users[0].name) # 'Alice'
print(data.users[0].scores[1]) # 87
print(data.users[0].metadata.tags[0]) # 'admin'
print(data.settings.theme) # 'dark'Perfect for working with JSON data:
import json
from dotdict3 import DotDict
# Load JSON
with open('config.json') as f:
config = DotDict(json.load(f))
# Access nested configuration
db_host = config.database.host
api_key = config.api.credentials.keyfrom dotdict3 import DotDict
data = DotDict({})
# Add nested structure dynamically
data['user'] = {
'profile': {
'name': 'Alice',
'preferences': ['email', 'sms']
}
}
# Automatically converted
print(data.user.profile.name) # 'Alice'
print(data.user.profile.preferences[0]) # 'email'Be careful with dictionary method names. Since DotDict inherits from dict, built-in method names will shadow your keys:
data = DotDict({'items': [1, 2, 3]})
# This returns the dict.items() method, not your data!
print(data.items) # <built-in method items>
# Use bracket notation instead
print(data['items']) # DotList([1, 2, 3])Common reserved names to avoid:
clearcopygetitemskeyspopsetdefaultupdatevalues
The classes include automatic type checking to prevent double conversion:
from dotdict3 import DotDict
inner = DotDict({'a': 1})
outer = DotDict({'inner': inner})
# inner is not converted again
assert outer['inner'] is inner # TrueAll iterables (except strings and bytes) are converted to DotList:
data = DotDict({
'list': [1, 2, 3],
'tuple': (4, 5, 6),
'set': {7, 8, 9},
'range': range(10, 13)
})
# All become DotList
assert isinstance(data['list'], DotList) # True
assert isinstance(data['tuple'], DotList) # True
assert isinstance(data['set'], DotList) # True
assert isinstance(data['range'], DotList) # True- Python 3.6+
- Compatible with all standard
dictandlistoperations
- Configuration management: Easy access to nested config files
- API responses: Simplify working with JSON API responses
- Data processing: Cleaner code when working with nested data structures
- Settings objects: Create intuitive settings/options objects
- String keys only (dictionary keys must be valid Python identifiers for dot notation)
- Watch out for reserved dictionary/list method names
- Dot notation won't work for keys with spaces or special characters
MIT License
Contributions are welcome! Please feel free to submit a Pull Request.
from dotdict3 import DotDict
import yaml
with open('config.yaml') as f:
config = DotDict(yaml.safe_load(f))
# Easy access to nested config
database_url = f"{config.database.host}:{config.database.port}"
log_level = config.logging.levelfrom dotdict3 import DotDict
import requests
response = requests.get('https://api.example.com/users/1')
user = DotDict(response.json())
print(f"{user.name} - {user.email}")
print(f"City: {user.address.city}")from dotdict3 import DotDict
test_data = DotDict({
'scenarios': [
{'name': 'happy_path', 'expected': 200},
{'name': 'not_found', 'expected': 404},
{'name': 'server_error', 'expected': 500}
]
})
for scenario in test_data.scenarios:
assert response.status_code == scenario.expected