DEV Community

Reduce your Python code complexity with this simple trick

Ran Isenberg on January 29, 2023

As engineers, we should always strive to write simple code. One common pitfall which is typical to many programming languages and not only to ...
Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
siwhelan profile image
Simon Whelan

Great tip! Thank you for sharing

Collapse
 
miguelmj profile image
MiguelMJ

If you work with python 3.10, you should check out the new pattern matching!

Collapse
 
wiseai profile image
Mahmoud Harmouch

Yup! But, I think the main purpose of this tutorial is to highlight the power of Hash Maps over traditional conditional branching( whether using if-else or switch) given the fact that accessing key-val is O(1). Know what I am saying?

Collapse
 
miguelmj profile image
MiguelMJ

Yes, that's right. I just wanted to add another interesting and powerful alternative to whoever reads the post. Btw, isn't the access of a dictionary O(log(n)) ?

Thread Thread
 
wiseai profile image
Mahmoud Harmouch

Python 3.7 and later versions introduce a major new feature when it comes to dictionaries; they are ordered in the insertion sense order by default. This means that items added to a dictionary are stored in the same order as they were added, kinda like an append function, so items can be retrieved in a predictable order. Furthermore, since Python 3.8, this order is reversible, just like with collections.OrderedDict.

The introduction of this ordering feature has sparked debates) about its implications for other features introduced since Python 3.6, such as keyword arguments (kwargs) which are now ordered, and the namespace passed to a metaclass being ordered as well.

In theory, the newer version of the dictionary improved in terms of performance because it uses two arrays, one that contains pointers for all entries of that dictionary in order (PyDictKeyEntry), and the other one acts as a hash table that contains the indices of these elements.

So, python dictionaries are ordered hash tables that take a contiguous chunk of memory, allowing for an O(1) lookup by index on average. This means that the time it takes to look up an item in a dictionary is constant, regardless of the number of items present in the dictionary. This makes dictionaries highly efficient for various tasks. However, in the worst-case scenario, the lookup is O(n). The following image illustrates the time complexity of hash tables in general.


Hash table, Wikipedia.

In case of a collision occurs, the Open addressing method kicks in.

Each key-value pair entry is represented as a c struct with three fields as shown in the snippet below:

typedef struct {
    /* Cached hash code of me_key. */
    Py_hash_t me_hash;
    PyObject *me_key;
    PyObject *me_value; /* This field is only meaningful for combined tables */
} PyDictKeyEntry;
Enter fullscreen mode Exit fullscreen mode

Code taken from pycore_dict.

If you are looking at how the lookup function is implemented, you can refer to the official CPython implementation.

Thread Thread
 
miguelmj profile image
MiguelMJ

I had no idea! Thanks for the elaborated answer!

Collapse
 
wiseai profile image
Mahmoud Harmouch

Throwback to my early days doing Open Source. It feels like ages ago.