A custom wrapper object around dict that allows attribute-style access to dictionary items and support for serialization of nested data.
Project description
objdict-bf
objdict-bf
is a Python module that provides a wrapper class to conveniently manipulate dictionaries or dict-based nested structures using attribute-like syntax. It is intended mostly to ease manipulation of serialized data, web requests and responses, configuration files, dynamic prototyping...
Features
- Attribute-style access to dictionary items (e.g.,
obj.key
instead ofobj['key']
). - Synchronization with the original dictionary if passed at instantiation.
- Utility methods for recursive conversion of nested structures to and from
objdict
anddict
. - Serialization and deserialization methods for both strings and files with json, toml, yaml and jsonpickle backend support.
- Advanced default value attribution features for missing keys.
- optional object-like behavior, by auto-passing the objdict instance as 'self' to callable attributes having 'self' in their signature.
Installation
pip install objdict-bf
Signature of the constructor
objdict(*args,_use_default=False,_default=None,_file=None,_backend=None,_auto_self=False,**kwargs)
Parameters:
*args
: either dicts, objdicts or iterables on key:value pairs. If the first arg is a dict, it will serve as the internal _data_dict of the objdict instance._use_default
: boolean, determines if a default value is attributed to missing keys_default
: can be any value or callable. If it is callable with adequate signature, this callable will be used to handle default values generation._file
: reference to a file path for dumping (extension must match the backend used)_backend
: either 'json', 'toml','yaml' or 'jsonpickle'. Determines the backend used for serialization/deserialization when dumping/loading (None defaults to 'json')._auto_self
: boolean. Determines if the instance is auto-passed a 'self' to its callable attributes having 'self' in their signature (mocked object behavior).**kwargs
: key value pairs passed as kwargs to update the objdict
Usage
from objdict_bf import objdict
# Create an objdict with some initial data
data = objdict(
name='John',
age=30,
location='New York'
)
#Or synchronize with an existing dict
d={'name': 'John', 'age': 30, 'location': 'New York'}
data = objdict(d)
#Access data using attribute-style access
print(data.name) # Output: John
print(data.age) # Output: 30
#Modify data
data.age = 31
#Create a new key:value pair
data.job='developer'
#Changes are reflected on the original dict
print(d['age']) #Ouput: 31
print(d['job']) #Ouput: 'developer'
#Chaining attributes is supported for nested structures involving lists
d={
'profile':{
'name':'John',
'hobbies':[
{'type':'sport','title':'tennis'},
{'type':'music','title':'guitar playing'}
]
}
}
data = objdict(d)
print(data) #Output: the repr of the above dict
print(data.profile.hobbies[1].title) #Output: guitar playing
#Conversion of dict items to their objdict version is automatic upon access.
#The created objdicts will inherit the parent objdict settings, namely: _backend,_use_default, _default, _auto_self).
#The objdict being essentially a wrapper interface on the initial dict,
#this conversion is reflected in the initial dict content as well
print(isinstance(data.profile.hobbies[1],objdict)) #Output: True
print(isinstance(d['profile']['hobbies'][1],objdict)) #Output: True
#to_dict returns the underlying dict, converting recursively all objdicts found in the nested structure back to dicts
print(d is data.to_dict()) #Ouptut: True
print(isinstance(d['profile']['hobbies'][1], dict)) #Output: True
#-----------------------------Serialization-------------------------------
# Serialize to JSON string
json_string = data.dumps()
print(json_string)
#or use another backend for serialization
toml_string=data.dumps(_backend='toml')
print(toml_string)
#dump to a file
data.dump("my_json_file.json")
#or
data.dump("my_toml_file.toml",_backend='toml')
#make some more changes
data.email="dummy.email@gmail.com"
#the reference to the file and backend preference from the last dump is kept in the objdict instance so you don't have to pass them again
data.dump()
# Deserialize from a string (new instance keeping reference to the chosen backend)
data = objdict.loads(json_string)
#or
data = objdict.loads(toml_string,_backend='toml')
# Deserialize from a file (new instance keeping reference to the chosen file and backend)
data = objdict.load("my_json_file.json")
#or
data = objdict.load("my_toml_file.toml",_backend='toml')
#Mismatching file extension and backend will throw an exception
#any class method creating a new instance can be passed parameters accepted in the objdict constructor to control the properties of the created instance:
data = objdict.loads(string,_backend='json',_use_default=True,_default=None,_auto_self=False)
data = objdict.load(file,_backend='toml',_use_default=False,_auto_self=True)
#update data
data.email="dummy.email@gmail.com"
data.user="dummy_username"
#dump changes to the file using the previously chosen backend
data.dump()
#-------------------Working with default value generators-------------------
#Default value (None) when accessing a missing key
obj=objdict(_use_default=True)
#Will set the value to None and won't raise a KeyError
print(obj.a) #Output: None
#Or, choose a default value
obj=objdict(_use_default=True,_default=3)
#Missing key will be initialized to 3 and returned
print(obj.a) #Output: 3
#Or pass a default value generator depending on the key (must have 'key' in its signature)
default_gen=lambda key: f"Missing key: {key}"
obj=objdict(_use_default=True,_default=default_gen)
print(obj.a) #Output: "Missing key: a"
print(obj.b) #Output: "Missing key: b"
#Or pass a default value generator whose output depends on the current state/content of the objdict
#Must have 'self' in its signature
#Will use 'self' as the keyword refering to the current objdict instance
def default_gen(self):
if 'a' in self:
return self.a.value
else:
return objdict(value=5)
obj=objdict(_use_default=True,_default=default_gen)
print(obj.a) #Output: {'value':5}
print(obj.b) #Output: 5
#Accepted signature of default value generators are () ; (self,); (key,) ; (self,key)
#This allows implementing context-aware and key-dependant logic for default value attribution.
#Any other signature will be considered invalid and will fall back to assign the callable itself as the default value for all keys.
#Example: Using a default value generator to automatically create new child objdict instances inheriting the parent's settings when accessing missing keys
def child_instance(self):
return objdict(_use_default=True,_default=child_instance,_backend=self._backend,_auto_self=self._auto_self)
obj=objdict(_use_default=True,_default=child_instance,_backend='toml',_auto_self=True)
obj.a.b.c=3
print(obj) #Output: {'a':{'b':{'c':3}}}
#child elements inherit the chosen parent properties
print(obj.a.b._backend) #Output: 'toml'
print(obj.a.b._auto_self) #Output: True
#The child_instance generator hard-coded above is already implemented as the objdict.child_instance static method which you may pass as _default parameter
obj=objdict(_use_default=True,_default=objdict.child_instance)
obj.a.b.c=3
print(obj) #Output: {'a':{'b':{'c':3}}}
#--------------------------------Mock objects-------------------------------
#Using the objdict as a mocked object with context aware methods thanks to the _auto_self parameter which automatically passes the objdict instance as 'self' to callable attributes having 'self' as first parameter in their signature.
obj=objdict(_auto_self=True)
obj.a=2
#create a function with 'self' as first parameter (any other name won't receive the instance)
def add_to_a(self,b):
self.a+=b
#attach the function as attribute
obj.add_to_a=add_to_a
obj.add_to_a(3)
print(obj.a) #output 5
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
objdict_bf-0.1.24.tar.gz
(12.0 kB
view details)
Built Distribution
File details
Details for the file objdict_bf-0.1.24.tar.gz
.
File metadata
- Download URL: objdict_bf-0.1.24.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | bd5c76495bb18fc6d36ee5c089ca4c17309356f64c01df3d9a61c336a24f69fe |
|
MD5 | 0529be31fce42df98e2e361210f284f8 |
|
BLAKE2b-256 | 9e466b4582171b00f79a8c40346cc8a23a782afad51b759814796590b3ad850a |
File details
Details for the file objdict_bf-0.1.24-py3-none-any.whl
.
File metadata
- Download URL: objdict_bf-0.1.24-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.10.12
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 62baae1fe236cdec2f624588813322d97c2b48b6add5e13fdba2e83ed3662ca5 |
|
MD5 | 9484c01ec88866fb92eb283bb1778a7a |
|
BLAKE2b-256 | 5a87a4f262ca9b3045c3e92fd44117192bfc6ef7a876faaa23dd0f82b21172a1 |