Package: litebox
LiteBoxContainers for finding Python objects by attribute. Backed by SQLite.pip install liteboxUsagefrom litebox import LiteBox
tb = LiteBox(
    [{'num': 1, 'size': 1000, 'shape': 'square'}],   # provide a collection of objects or dicts 
    {'size': int, 'shape': str})                     # specify attributes to store
tb.find('size >= 1000 and shape == "square"')        # find by attribute valueThe objects can be anything -class,dataclass,namedtuple,dict,string,int, etc.LiteBox supportsadd(),add_many(),update(), andremove(); see API below.Nested attributesYou can define a function to access nested or derived attributes.from litebox import LiteBox

objs = [
    {'num': 1, 'nested': {'a': 2, 'b': 3}}, 
    {'num': 2, 'nested': {'a': 4, 'b': 5}}
]

def nested_attr(obj):
    return obj['nested']['a']

# Build LiteBox, run find
tb = LiteBox(objs, {nested_attr: int})
tb.find('nested_attr == 2')  # returns obj 1How it worksWhen you do:LiteBox(list_of_objects, on={'size': int, 'shape': string})orPandasBox(...)A SQLite table is created with 3 columns:sizeshapePython object referenceOnfind(), a query will run to find the matching objects.Only the relevant attributes of the object are copied into the table. The rest of the object remains in memory.An ideal use case is when you have "heavy" objects containing images / audio / large texts, plus some small
metadata fields that you want to find by. Just make a LiteBox on the metadata, and use it to find
the object without needing to serialize / deserialize the heavy stuff.LiteBox is especially good when finding by<and>. If you only need==, considerHashBox-- it is based on dict lookups which are faster in that case.APIInitLiteBox(
        objs: Optional[Iterable[Any]] = None,
        on: Optional[Dict[str, Any]] = None,
        index: Optional[List[ Union[Tuple[str], str]]] = None
)Creates a LiteBox.objsis optional. It can be any container of class, dataclass, dict, or namedtuple objects.onis required. It specifies the attributes and types to index. The allowed types are float, int, bool, and str.indexspecifies the indices to create on the SQLite table. If unspecified, a single-column index is made on each
attribute.Theindexparameter is the key to getting good performance. A multi-column index can often speed upfind()operations.index=[('a', 'b', 'c'), 'd']will create a multi-column index on(a, b, c)and a single-column index
ond.  Conversely, some columns such as those containing only a few different values may perform better without an
index.SeeSQLite index documentationfor more insights.find()find(where: Optional[str]) -> Listfinds objects matching the query string inwhere.Examples:tb.find('b == True and string == "okay"')tb.find('(x == 0 and y >= 1000.0) or x == 9')lb.find('x is null')Ifwhereis unspecified, all objects in the container are returned.Consult the syntax forSQLite queriesas needed.add(), add_many()add(obj:Any)
add_many(objs:Iterable[Any])Theadd()method adds a single object. If you have many objects, it is much faster toadd_many()than it is to
calladd()on each one.If an added object is missing an attribute, the object will still be added. The missing attribute will be given aNonevalue.update()update(self, obj: Any)updates attributes of a single object in the index.
It's just a shorthand forremove()and thenadd().If you change an object's attributes without callingupdate(), the LiteBox will be out of sync and
return stale results. Consider implementing asetattrlistener on your object to update LiteBox when your objects
change.remove()remove(self, obj: Any)removes an object.Container methodsYou can do the usual container things:Length:len(tb)Contains:obj in tbIteration:for obj in tb: ...PerformanceSeeexamplesfor performance tests.