Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Explore Python Libraries: Immutables

Jul 8, 2020 • 5 Minute Read

Introduction

When operating on data, it is extremely important that you guard yourself against data mutations, which can cause both your data and the system your data is funneling through to become more complex than necessary. In this guide, you will learn about the Python Immutables library and how you can use the data structure this library provides to efficiently, and immutably, operate on data using a number of both individual and batch operations. This library will help you to reason about your data simply. Let's get started!

Getting Started Using Immutables

The Python Immutables library provides an immutable API on top of the Hash Array Mapped Trie data structure. A Hash Array Mapped Trie (HAMT for short) is essentially an associative array that uses elements of both the hash tables and trie data structures.

So why use a HAMT? The primary reason to use this data structure is that it is incredibly memory efficient compared to similar data structures when working on large amounts of data. The HAMT achieves this by sacrificing a bit of speed on the lookup time of its keys. In the worst case, the HAMT has an access time of O(logn) (compare this to a hash table's O(1) access time). But this isn't all! This library doesn't just implement the HAMT, but it also provides an immutable and memory-efficient API on top of it.

In the following sections, you will uncover the API that this library exposes on top of its HAMT implementation.

Installation

The Immutables library requires Python version 3.5 or greater and is available to download using pip or conda.

To install using pip:

      pip install immutables
    

To install using conda:

      conda install -c anaconda immutables
    

Once the library is downloaded, you can import it and start using the supplied Map type like this:

      import immutables

    my_map = immutables.Map(key1='a', key2='b', key3='c')

    my_second_map = immutables.Map({'a': 10, 'b': 20}, c=30)
    

Individual Operations

The Map type provided by the Immutables library provides a number of individual operations. These operations include: - get() - set() - delete()

The get operation allows you to grab a single value from the Map via a unique key. An example of this looks like:

      my_map.get('key1')

    # Will fetch the value 'a' from my_map
    

The get method also allows you to specify a fallback in case the key you supply does not exist on the Map. This looks like:

      my_map.get('bogus_key', 5)

    # Will fetch the value 5 from my_map
    

The next individual operation supplied on the Map type is the set method. The set method takes a key and a value and adds an entry to the Map. Usage of this method looks like:

      my_map.set('my_key', 1)
    

Finally, the delete method takes in a key and removes the corresponding entry within the Map. Usage of this method looks like:

      keys_to_remove = list('key1', 'key2')

    for key in keys_to_remove:
        my_map.delete(key)
    

A key component concerning the set and delete operations is their immutability. Each of these methods returns a new Map, and the previous map is left untouched. Here is an example:

      my_map = immutables.Map(key1='a', key2='b', key3='c')

    my_new_map = my_map.delete('key1')

    print(my_map.get('key1'))
    # prints 'a'

    print(my_new_map.get('key1', 'fallback'))
    # prints 'fallback'
    

Batch Operations

Individual operations are great and very useful. However, what if you want to apply a number of mutations in sequence without having to create an entirely new Map after each operation? Enter mutate and finish. The mutate and finish methods allow you to apply bulk updates or alterations to a Map. This API works like this:

      my_map = immutables.Map(key1='a', key2='b', key3='c')
    
    map_mutation_context = my_map.mutate()
    map_mutation_context['new_key'] = 1
    map_mutation_context.set('key1', 'b')
    map_mutation_context.delete('key2')

    my_new_altered_map = map_mutation_context.finish()

    # The above code can also be written as:

    with my_map.mutate() as map_mutation_context:
        map_mutation_context['new_key'] = 1
        map_mutation_context.set('key1', 'b')
        map_mutation_context.delete('key2')
        my_new_altered_map = map_mutation_context.finish()
    

Conclusion

In this guide, you learned about the Python Immutables library in depth. You should now have a solid grasp of how to use the efficient, Hash Array Mapped Trie data structure that this library provides. You can instantiate the provided Map type as well as the individual and batch operations this immutable type provides.

This library will help you to reason about data within your Python programs in an idiomatic and safe manner. For more information, you can always check out the library docs.

Zachary Bennett

Zachary B.

Zach is currently a Lead Software Developer at OpalSoft where he uses tools such as Scala, TypeScript, Python, Docker, Node, and Angular. Zach has a passion for GIS programming along with open-source software. You can view some of his work on GitHub (https://github.com/zbennett10) and Stack Overflow (https://stackoverflow.com/users/6879849/zachary-bennett).

More about this author