Skip to content

Bunch is Slow — Add test for relative performance #31

@jayd3e

Description

@jayd3e

I liked AttrDict. Then AttrDict was too slow. I liked Bunch. Then Bunch was too slow. These two libraries ended up consuming 1/2 and 1/3 of my request time when using them frequently, for what I had assumed was a very simple use-case. The main problem comes from two things 1) Bunch inherits from dict, so it brings along the entire dict implementation, which isn't always necessary(rarely is in fact). 2)bunchify aggressively goes through an entire nested dictionary, and puts copies of Bunch everywhere. This results in attributes that may never get called, getting implanted with the Bunch object.

Speeding this up proved to not take a lot of code. Check out this gist from @mmerickel, which I've copied below. It uses a light-weight DictProxy object to mediate access to the object, and it also lazy-loads each attribute. This decreased the amount of time that my app spend "Bunching" objects to relatively nothing, from 1/3 of my request time using Bunch master.

class DictProxy(object):
    """
    A proxy for a dictionary that allows attribute access to underlying keys.

    You may pass a custom ``wrapper`` to override the logic for wrapping
    various custom types.

    """
    def __init__(self, obj, wrapper=wrap):
        self.obj = obj

    def __getitem__(self, key):
        return self.wrapper(self.obj[key])

    def __getattr__(self, key):
        try:
            return self.wrapper(getattr(self.obj, key))
        except AttributeError:
            try:
                return self[key]
            except KeyError:
                raise AttributeError(key)

    # you probably also want to proxy important list properties along like
    # items(), iteritems() and __len__

class ListProxy(object):
    """
    A proxy for a list that allows for wrapping items.

    You may pass a custom ``wrapper`` to override the logic for wrapping
    various custom types.

    """
    def __init__(self, obj, wrapper=wrap):
        self.obj = obj

    def __getitem__(self, key):
        return self.wrapper(self.obj[key])

    # you probably also want to proxy important list properties along like
    # __iter__ and __len__

def wrap(value):
    """
    The top-level API for wrapping an arbitrary object.

    This only works for ``dict``, ``list`` and ``tuple`` types. If you want
    to wrap other types you may write your own wrap and pass ``wrapper=`` to
    ``DictProxy`` and ``ListProxy``.

    """
    if isinstance(value, dict):
        return DictProxy(value)
    if isinstance(value, (tuple, list)):
        return ListProxy(value)
    return value

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions