Python 3.9 Features for Noobs
Tags: python, tutorial, codenewbie, programming
Published: 2021-09-12 18:51:19

In this post, I will discuss some of the interesting features added in python 3.9!
## The Problem: Merging two dictionaries

Prior to python 3.9, there were mainly two ways to merge or combine two `dictionaries` in python,

```python
dict1 = {"name": "Salman", "age": "NA"}
dict2 = {"crimes": "lol", "career_destroyed": "uncountable"}

# Way1 : using update method
dict1.update(dict2)
print("dict1 =>", dict1)

# Way2: using ugly ** unpacking
new_dict = {**dict1, **dict2}
print("new_dict=>", new_dict)

"""OUPUT
dict1 => {'name': 'Salman', 'age': 'NA', 
'crimes': 'lol', 'career_destroyed': 'uncountable'}
new_dict=> {'name': 'Salman', 'age': 'NA',
'crimes': 'lol', 'career_destroyed': 'uncountable'}

"""
```
But using these methods, have drawbacks.
### Why `update` is not efficient ?

`d1.update(d2)` modifies d1 _in-place_ i.e changes the content of d1 dictionary.

But the problem is, it is using a *temporary* variable internally to update d1 dict. This is very inefficient because consider a case when you want to merge two dictionaries containing thousands of key-value pair, It is a brutal waste of memory and CPU!
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s64aifdtffibgq859dpq.png)
 
> 😛 Then why don't we use ** , because it is not readable and ugly!

## Solution : Addition of Union Operators
> 📃 https://www.python.org/dev/peps/pep-0584/

Let's see how python3.9 solved the issue,

### Introducing merge operator `|` 

The merge operator , "|" will return a new `dict` consisting of the left operand merged with the right operand, each of which must be a dict.

This operation has a fancy name : **Dict Union**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ydqy4526hx7xux1xpowa.png)
 

### update operator `|=`

This operator performs an _inplace merge_ similar to update method but it doesn't waste memory by using a temporary variable!

Fancy name : **Augmented assignment**

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u5ockqx8nqf2iamdrme.png)

## The Problem: How to remove sub-strings from start or end in a `str` ?
Prior to python3.9, you would do something like this, 
```python
# removing prefix or suffix prior to python 3.9
def remove_prefix(inp_str: str, pref: str) -> str:
    """
    This function deletes "pref" from the 
    beginning if it is present otherwise
    returns the original "inp_str"
    """
    if inp_str.startswith(pref):
        return inp_str[len(pref):]
    return inp_str

def remove_suffix(inp_str: str, suff: str) -> str:
    """
    This function deletes "suff" from the 
    end if it is present otherwise
    returns the original "inp_str"
    """
    if inp_str.endswith(suff):
        return inp_str[:-len(suff)]
    return inp_str

"""OUTPUT
>>> remove_suffix("Kathan", "an")
'Kath'
>>> remove_suffix("Kathan", "lol")
'Kathan'
>>> remove_prefix("Kathan","Ka")
'than'
>>> remove_prefix("Kathan","haha")
'Kathan'
"""
```
Isn't it too much, that  we need to define functions for such a simple task? Ofcourse you can create a one-liner using `lambda` but what if you want a cleaner solution? 

## Solution : String methods to remove prefixes and suffixes 
>📃 https://www.python.org/dev/peps/pep-0616/

To solve this issue, 2 new methods were added.

### `removeprefix` method

This method has implemented the prior discussed functionality of removing prefix for us so that we don't need to define our own function or lambda whatever.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ozj98ru60612jlw8mko.png)

### `removesuffix` method
This method has implemented the prior discussed functionality of removing suffix for us so that we don't need to define our own function.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwzupr454onmkvzd8luz.png)
 
## 🎁 Bonus : Improved Math Module

> 📃 https://docs.python.org/3/whatsnew/3.9.html#math
 
### `math.gcd()` function
Prior to python 3.9, this function only accepted _two_ arguments and returned their gcd.

But quite often we need to find gcd of more than two numbers. GOOD NEWS! In python 3.9 you can add as many arguments you want into this function.

Here's the error by the way, you used to get prior to python 3.9
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/665b22dlxos5uaej609c.png)
 

### Using improved `math.gcd` function

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8h8zriql7uazjx6ae2ic.png)
 
### The new `math.lcm()` function

Prior to python 3.9 there was no specifc function to calculate l.c.m. 

Developers though had a quirky workaround for finding l.c.m,
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufyw06lpsf443ojropo5.png)
 
But if you ain't a math geek, you will be confused by the function 😓, So there was a need for l.c.m function!


### NEW `math.lcm()` function

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssldjcr12rqwmd71g8yk.png
)

That's it for this post :) Hope you enjoyed it. Check python 3.9 docs for lots of more fun stuff!

>📚 Further Read: https://docs.python.org/3/whatsnew/3.9.html#what-s-new-in-python-3-9