Skip to main content
https://raw.githubusercontent.com/ducdetronquito/scalpl/master/assets/scalpl.png

Scalpl

https://img.shields.io/badge/license-public%20domain-ff69b4.svg https://img.shields.io/badge/coverage-100%25-green.svg https://img.shields.io/badge/pypi-v0.4.2-blue.svg https://travis-ci.org/ducdetronquito/scalpl.svg?branch=master

Outline

  1. Overview

  2. Benefits

  3. Installation

  4. Usage

  5. Benchmark

  6. Frequently Asked Questions

  7. How to Contribute

  8. License

Overview

Scalpl provides a lightweight wrapper that helps you to operate on nested dictionaries seamlessly through the built-in dict API, by using dot-separated string keys.

It’s not a drop-in replacement for your dictionnaries, just syntactic sugar to avoid this['annoying']['kind']['of']['things'] and prefer['a.different.approach'].

No conversion cost, a thin computation overhead: that’s Scalpl in a nutshell.

Benefits

There are a lot of good libraries to operate on nested dictionaries, such as Addict or Box , but if you give Scalpl a try, you will find it:

  • 🚀 Powerful as the standard dict API

  • ⚡ Lightweight

  • 👌 Well tested

Installation

Scalpl is a Python3 library that you can install via pip

pip3 install scalpl

Usage

Scalpl provides a simple class named Cut that wraps around your dictionary and handles operations on nested dict and that can cut accross list item.

This wrapper strictly follows the standard dict API, which means you can operate seamlessly on dict, collections.defaultdict or collections.OrderedDict by using their methods with dot-separated keys.

Let’s see what it looks like with an example ! 👇

from scalpl import Cut

data = {
    'pokemon': [
        {
            'name': 'Bulbasaur',
            'type': ['Grass', 'Poison'],
            'category': 'Seed',
            'ability': 'Overgrow'
        },
        {
            'name': 'Charmander',
            'type': 'Fire',
            'category': 'Lizard',
            'ability': 'Blaze',
        },
        {
            'name': 'Squirtle',
            'type': 'Water',
            'category': 'Tiny Turtle',
            'ability': 'Torrent',
        }
    ],
    'trainers': [
        {
            'name': 'Ash',
            'hometown': 'Pallet Town'
        }
    ]
}
# Just wrap your data, and you're ready to go deeper !
proxy = Cut(data)

You can use the built-in dict API to access its values.

proxy['pokemon[0].name']
# 'Bulbasaur'
proxy.get('pokemon[1].sex', 'Unknown')
# 'Unknown'
'trainers[0].hometown' in proxy
# True

By default, Scalpl uses dot as a key separator, but you are free to use a different character that better suits your needs.

# You just have to provide one when you wrap your data.
proxy = Cut(data, sep='->')
# Yarrr!
proxy['pokemon[0]->name']

You can also easily create or update any key/value pair.

proxy['pokemon[1].weaknesses'] = ['Ground', 'Rock', 'Water']
proxy['pokemon[1].weaknesses']
# ['Ground', 'Rock', 'Water']
proxy.update({
    'trainers[0].region': 'Kanto',
})

Following its purpose in the standard API, the setdefault method allows you to create any missing dictionary when you try to access a nested key.

proxy.setdefault('pokemon[2].moves.Scratch.power', 40)
# 40

And it is still possible to iterate over your data.

proxy.items()
# [('pokemon', [...]), ('trainers', [...])]
proxy.keys()
# ['pokemon', 'trainers']
proxy.values()
# [[...], [...]]

By the way, if you have to operate on a list of dictionaries, the Cut.all method is what you are looking for.

# Let's teach these pokemon some sick moves !
for pokemon in proxy.all('pokemon'):
    pokemon.setdefault('moves.Scratch.power', 40)

Also, you can remove a specific or an arbitrary key/value pair.

proxy.pop('pokemon[0].category')
# 'Seed'
proxy.popitem()
# ('trainers', [...])
del proxy['pokemon[1].type']

Because Scalpl is only a wrapper around your data, it means you can get it back at will without any conversion cost. If you use an external API that operates on dictionary, it will just work.

import json
json.dumps(proxy.data)
# "{'pokemon': [...]}"

Finally, you can retrieve a shallow copy of the inner dictionary or remove all keys.

shallow_copy = proxy.copy()

proxy.clear()

Benchmark

This humble benchmark is an attempt to give you an overview of the performance of Scalpl compared to Addict, Box and the built-in dict.

It will summarize the number of operations per second that each library is able to perform on a portion of the JSON dump of the Python subreddit main page.

You can run this benchmark on your machine with the following command:

python3 ./benchmarks/performance_comparison.py

Here are the results obtained on an Intel Core i5-7500U CPU (2.50GHz) with Python 3.6.4.

Addict 2.2.1:

instantiate:-------- 271,132  ops per second.
get:---------------- 276,090  ops per second.
get through list:--- 293,773  ops per second.
set:---------------- 300,324  ops per second.
set through list:--- 282,149  ops per second.

Box 3.4.2:

instantiate:--------- 4,093,439  ops per second.
get:-----------------   957,069  ops per second.
get through list:----   164,013  ops per second.
set:-----------------   900,466  ops per second.
set through list:----   165,522  ops per second.

Scalpl latest:

instantiate:-------- 183,879,865  ops per second.
get:----------------  14,941,355  ops per second.
get through list:---  14,175,349  ops per second.
set:----------------  11,320,968  ops per second.
set through list:---  11,956,001  ops per second.

dict:

instantiate:---------  37,816,714  ops per second.
get:-----------------  84,317,032  ops per second.
get through list:----  62,480,474  ops per second.
set:----------------- 146,484,375  ops per second.
set through list :--- 122,473,974  ops per second.

As a conclusion and despite being an order of magniture slower than the built-in dict, Scalpl is faster than Box and Addict by an order of magnitude for any operations. Besides, the gap increase in favor of Scalpl when wrapping large dictionaries.

Keeping in mind that this benchmark may vary depending on your use-case, it is very unlikely that Scalpl will become a bottleneck of your application.

Frequently Asked Questions:

  • What if my keys contain dots ?

    If your keys contain a lot of dots, you should use an other key separator when wrapping your data:

    proxy = Cut(data, sep='->')
    proxy['computer->network->127.0.0.1']

    Otherwise, split your key in two part:

    proxy = Cut(data)
    proxy['computer.network']['127.0.0.1']
  • What if my keys contain spaces ?:

    proxy = Cut(data)
    proxy['it works perfectly'] = 'fine'

How to Contribute

Contributions are welcomed and anyone can feel free to submit a patch, report a bug or ask for a feature. Please open an issue first in order to encourage and keep tracks of potential discussions ✍️

License

Scalpl is released into the Public Domain. 🎉

Ps: If we meet some day, and you think this small stuff worths it, you can give me a beer, a coffee or a high-five in return: I would be really happy to share a moment with you ! 🍻

Metadata

Release files for scalpl 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scalpl 0.4.2
File Size Uploaded
scalpl-0.4.2.tar.gz 8.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for scalpl 0.4.2
File Interpreter ABI Platform
scalpl-0.4.2-py3-none-any.whl Python 3 none any Details

Total release size: 15.7 kB

Release files / scalpl-0.4.2.tar.gz

Download URL scalpl-0.4.2.tar.gz
Size 8.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8773a1c91e051a4baa255acf1804fe331fae6a36501c99519415511456b8a745
BLAKE2b-256 checksum
How to use checksums
b283477ac6623a5c97eff60ee27666f144ca0f1c0627e6f58b095e43c72829f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.1 importlib_metadata/3.10.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.9.1

Release files / scalpl-0.4.2-py3-none-any.whl

Download URL scalpl-0.4.2-py3-none-any.whl
Size 6.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a7a963b9b3639108722008cc2b5a42ae576b48c74fcd0f17baf7b1c9792ab7b
BLAKE2b-256 checksum
How to use checksums
7d9ac0f48ff733cd14e1a742409d829e308900059723fb1f00dc98f0f4012ed2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.1 importlib_metadata/3.10.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.9.1

Release history Release notifications | RSS feed

This release

0.4.2 This release

2 release files

0.4.1

2 release files

0.4.0

1 release file

0.3.0

1 release file

0.2.6

2 release files

0.2.5

1 release file

0.2.4

1 release file

0.2.3

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page