# Python tips

### The Walrus(:=) Operator

This is one of the latest additions to Python. The walrus operator was added to Python in version 3.8. It is basically an assignment expression that allows assignment directly in the expression. Usual way:

``````a = [1,2,3]
b=len(a)
if b > 2:
print(b)

``````

In the above-mentioned example, we declared a list. Then we declared a variable ‘b’ to assign the value of the length of the list. But by using the walrus operator:

``````a = [1,2,3]
if (b := len(a) > 2):
print(b)

``````

Here, we declared and assigned the value at the same time. This method helps to decrease the lines of code too.

### Dividing large numbers in chunks

Let's talk with practicality. Take a look at the numbers below and tell me, which number would you be able to understand and say faster? • 7683423898 • 1,989,738,100 Obviously, you guys are gonna answer that the second one is easier to understand. Why is that? That's because it is divided into thirds. Everybody understands basic maths and knows that the fourth part is the billions. Therefore the number is one billion, nine hundred eighty-nine million, seven hundred thirty-eight thousand, one hundred. This division makes it much faster to understand which part is the billions and which is the millions etc. In Python, you can place underscores in numbers so you can use them to divide up any large number. This will help improve the readability of the data entered as well as the code. Example:

``````a = 122_343_090
print(a*2 + 32_400)

# or

b = (4_2_8_9) # Only a psychopath is gonna do this🙄

``````

### Replacing nested ifs with if/continue

Consider the following example: we want to loop through each element in a 3 by 3 matrix, which is done with two "for loops". If the element satisfies a certain condition, we’ll perform some action with or on the numbers. Although this example is way too basic, writing an if statement in this manner makes your code a lot less readable. This is especially the case if there are already many indents before and after so it’s safe to say that it is almost always better to reduce the amount of necessary indentation in our code. If there is only a need to include an if statement but not an elif or else component, we can write the code another way. If the condition is not true, we call the continue keyword, which will skip anything left in the iteration and then continues to the next. This method is very helpful in making complex code more readable!!!

### Simplify an "if statement"

Sometimes you have to verify multiple values. What's the method you use? This one seems legit, right?

``````if a == 1 or a == 3 or a == 5 or a == 7:

``````

Guess what, you can also do it like this:

``````if a in [1,3,5,7]:

``````

This method makes it much easier to understand the condition, resulting in a readable code.

### Assigning multiple variables in 1 line

Sometimes, assigning a lot of variables takes up a lot of lines and all you want to do is make your code smaller. So here's how to assign variables in just 1 line, yes, you heard me right :)

``````a, b, c = 1, 2, 3

``````

It's as simple as that!

### Python easter eggs

Anti-gravity

``````import antigravity

antigravity.fly()
``````

The Zen of Python

``````>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one and preferably only one obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea let's do more of those!
``````

List object attributes

``````>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> dir("Hello World")
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
``````

### Printing the file path of imported modules

We always use modules such as os, pygame etc. Ever thought about where they come from? If you did then here's how to find out!

``````import os
import pygame

print(os)
print(pygame)

``````

Output:

``````<module 'os' from 'C:\\Users\\Dell\\AppData\\Local\\Programs\\Python\\Python39\\lib\\os.py'>
<module 'pygame' from 'C:\\Users\\Dell\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site-packages\\pygame\\__init__.py'>

``````

### Other Python tricks

Generating uuid.

``````# This creates a randomized 128-bit number that will almost certainly be unique.
# In fact, there are over 2¹²² possible UUIDs that can be generated. That’s over five undecillion (or 5,000,000,000,000,000,000,000,000,000,000,000,000).

>>> import uuid
>>> user_id = uuid.uuid4()
>>> user_id
UUID('7c2faedd-805a-478e-bd6a-7b26210425c7')
``````

Memoization using LRU cache.

``````import functools

@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n == 0:
return 0
elif n == 1:
return 1
return fibonacci(n - 1) + fibonacci(n - 2)
``````

Suppression of expressions

``````>>> from contextlib import suppress
>>> with contextlib.suppress(ZeroDivisionError):
>>>     10/0
# No exception raised
``````

An elegant way to deal with a file path (3.4≥)

``````>>> from pathlib import Path
>>> data_folder = Path("source_data/text_files/")

>>> file_to_open = data_folder / "raw_data.txt"
>>> file_to_open.name
"raw_data.txt"
>>> file_to_open.suffix
"txt"
>>>file_to_open.stem
"raw_data"

# Files functions
>>> f = open(file_to_open)
# content of the file
>>> file_to_open.exists()
True
``````

Creating decorator to separate concerns

``````>>>from functools import wraps

>>>    @wraps(wrapped)
>>>    def wrapper(*args, **kwargs):
>>>        return wrapped(*args, **kwargs) + ' sandwich'
>>>    return wrapper

>>>def ham():
>>>    return 'ham'

>>>ham()
'ham sandwich'
``````

Using yield to create a simple iterator

``````>>> def foo(lst):
>>>   for x in lst:
>>>       yield x
>>>       yield x*2

>>> a = [1, 3]
>>> list(foo(a))
[1, 2, 3, 6]
``````

### Python unpacking tricks

Unpack variables from iterable.

``````# One can unpack all iterables (tuples, list etc)
>>> a, b, c = 1, 2, 3
>>> a, b, c
(1, 2, 3)

>>> a, b, c = [1, 2, 3]
>>> a, b, c
(1, 2, 3)
``````

Swap variables values.

``````>>> a, b = 1, 2
>>> a, b = b, a
>>> a, b
(2, 1)
``````

Unpack variables from iterable without indicating all elements.

``````>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
``````

Unpack variables using the splat operator.

``````>>> def test(x, y, z):
>>>     print(x, y, z)
>>> res = test(*[10, 20, 30])
10 20 30
>>> res = test(**{'x': 1, 'y': 2, 'z': 3} )
10 20 30
``````

Flatten iterables.

``````>>> a = [[1, 2], [3, 4], [5, 6]]
>>> list(itertools.chain.from_iterable(a))
[1, 2, 3, 4, 5, 6]
``````

Creating cartesian products from iterables.

``````>>> for p in itertools.product([1, 2, 3], [4, 5]):
>>>    print(''.join(str(x) for x in p))

(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
(3, 5)
``````

Creating permutation from iterable.

``````>>> for p in itertools.permutations([1, 2, 3, 4]):
>>>      print(''.join(str(x) for x in p))
123
132
213
231
312
321
``````

Creating ngram from iterable.

``````>>> from itertools import islice
>>> def n_grams(a, n):
...     z = (islice(a, i, None) for i in range(n))
...     return zip(*z)
...
>>> a = [1, 2, 3, 4, 5, 6]
>>> n_grams(a, 3)
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]
>>> n_grams(a, 2)
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
>>> n_grams(a, 4)
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]
``````

Combining two iterables of tuples with padding or pivot nested iterable with padding.

``````>>> import itertools as it
>>> x = [1, 2, 3, 4, 5]
>>> y = ['a', 'b', 'c']
>>> list(zip(x, y))
[(1, 'a'), (2, 'b'), (3, 'c')]

>>> list(it.zip_longest(x, y))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
``````

Creating a combination of k things from an iterable of n

``````>>> import itertools
>>> bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
>>> list(itertools.combinations(bills, 3))
[(20, 20, 20), (20, 20, 10), (20, 20, 10), ... ]
``````

Creating accumulated results of iterable given a function

``````>>> import itertools
>>> list(itertools.accumulate([9, 21, 17, 5, 11, 12, 2, 6], min))
[9, 9, 9, 5, 5, 5, 2, 2]
``````

Creating an iterator that returns elements from the iterable as long as the predicate is true

``````>>> import itertools
>>> itertools.takewhile(lambda x: x < 3, [0, 1, 2, 3, 4])
[0, 1, 2]

>>> it.dropwhile(lambda x: x < 3, [0, 1, 2, 3, 4])
[3, 4]
``````

Creating an iterator that filters elements from iterable returning only those for which the predicate is False

``````>>> import itertools
# keeping only false values
>>> list(itertools.filterfalse(bool, [None, False, 1, 0, 10]))
[None, False, 0]
``````

Creating an iterator that computes the function using arguments obtained from the iterable of iterables

``````>>> import itertools
>>> import operator
>>> a = [(2, 6), (8, 4), (7, 3)]
>>> list(itertools.starmap(operator.mul, a))
[12, 32, 21]
``````

### Python comprehensions tricks

List comprehension.

``````>>> m = [x ** 2 for x in range(5)]
>>> m
[0, 1, 4, 9, 16]
``````

Set comprehension.

``````>>> m = {x ** 2 for x in range(5)}
>>> m
{0, 1, 4, 9, 16}
``````

Dict comprehension.

``````>>> m = {x: x ** 2 for x in range(5)}
>>> m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
``````

Generator comprehension.

``````# A generator comprehension is the lazy version of a list comprehension.
>>> m = (x ** 2 for x in range(5))
>>> m
<generator object <genexpr> at 0x108efe408>
>>> list(m)
[0, 1, 4, 9, 16]

>>> m = (x ** 2 for x in range(5))
>>> next(m)
0
>>> list(m)
[1, 4, 9, 16]
``````

List comprehension with the current and previous value.

``````>>> a = [1, 2, 4,2]
>>> [y - x for x,y in zip(a,a[1:])]
[1, 2, -2]
``````

Note: all comprehension can use predicates with if statement.