Skip to content

topper-123/freezedata

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Recursively convert list to tuple, set to frozenset, dict to mappingproxy etc.

Example usage:

import freezedata

data = [{'a': [1,2,3], 'b': {1,2,3}}, {1:1, 2:2, 3:3}]
frozendata = freezedata.freeze_data(data)
print(frozendata)
>> (mappingproxy({'a': (1, 2, 3), 'b': frozenset({1, 2, 3})}),

mappingproxy({1: 1, 2: 2, 3: 3}))

This is a read-only data structure, that is; there is no direct way to alter this data structure from within frozendata itself (without using some special modules (gc, inspect)).

For example:

frozendata[0]['a'][0] = 4
>> TypeError: 'tuple' object does not support item assignment
del frozendata[1][1]
>> TypeError: 'mappingproxy' object does not support item deletion

Notice: Since a mappingproxy is not hashable, frozen data structures containing mappingproxy (i.e. based on dict) will not be hashable either:

hash(frozendata)
>> TypeError: unhashable type: 'mappingproxy'

On the other hand, if the frozen data structure contains only hashable elements, the whole structure will be hashable (and immutable) as well:

frozendata = freezedata.freeze_data([[1,2,3], {4,5,6}])
print(frozendata)
>> ((1, 2, 3), frozenset({4, 5, 6}))
hash(frozendata)
>> -11948691520864899

Relaxing requirements (accepting functions, modules, classes and instances):

Functions, modules, (user-created) classes and instances are mutable in Python, and therefore neither immutable or read-only. By default, using these will result in errors, but setting parameter allow as one, several or all of functions, modules , classes and instances, these can be used in the new new data structure.

Functions have mutable attributes in Python, but sometimes you still want a function in a new data structure that won't affect the parent data structure / parent function. By setting allow='functions' or allow=['functions'], the new data structure will contain a copy of the included functions and its public attributes:

def func(n):
    return n*2
func.a = 'a'
data = [func]
frozendata = freezedata.freeze_data(data, allow='functions')
data[0] == frozendata[0]
>> False
frozendata[0].a = 'b'
print(data[0].a, frozendata[0].a)
>> a b

modules will be converted to a namedtuple, if you're freezing a module. If a module is in the data structure, but it's not top level, an error will by default be raised. If allow={'modules'} is set, non-top-level modules will be allowed and kept unchanged.

classes and class instances may be converted into namedtuple and used in the frozen data structure by setting allow={'classes', 'instances} or only one, e.g. allow={'classes'}, as needed. By converting to namedtuple, information may be lost, as attributes with leading underscores will be ignored:

class Test:
    a = 1
    def __init__(self, a):
        self.a = a
test = Test(2)
frozendata = freezedata.freeze_data([Test, test], allow={'classes', 'instances'})
print(frozendata)
>> (Test(a=1), Test(a=2))
print(type(frozendata[0]), type(frozendata[1]))
>> <class 'freezedata.freezedata.Test'> <class 'freezedata.freezedata.Test'>  # two namedtuples

About

Recursively convert lists to tuples, sets to frozensets, dicts to mappingproxy etc.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages