# 5 Patterns to Write Better and Clean Code in Python

·

## 1. Looping with Index

While working on problems like Two Sum, You need to iterate the list and get the index of items. You can get the index either using a counter or you can use range iterator to loop through the length of the list. You can also use enumerate to iterate an iterable object with an index.

Using range operator:

``````def twoSum(nums, target):
num_map = {}
for i in range(len(nums)):
if nums[i] in num_map:
return [num_map[nums[i]], i]
else:
num_map[target-nums[i]] = i
print(twoSum([2, 7, 11, 15], 9))
``````

Using enumerate:

``````def twoSum(nums, target):
num_map = {}
for index, num in enumerate(nums):
if num in num_map:
return [num_map[num], index]
else:
num_map[target-num] = index
``````

## 2. Slicing of Iterable

An iterable object can be iterated over using a loop. slice function returns a slice object, that can extract a sublist from an iterable object. Since a string is also an iterable object. Slicing can also be used on strings. The syntax of the slice is `slice(_start_, _stop_[, _step_]).`

``````nums = [1, 2, 3, 4, 5, 6]
name = "123456"
sslice = slice(1, 6, 2)
print(nums[slice(1, 6)]) # [1, 2, 3, 4, 5, 6]
print(nums[sslice]) # [2, 4, 6]
print(name[sslice]) # 246
`
``````

The above code can be simplified using square brackets. You can access `sublist` using square brackets.

Here start, stop and steps all are optional. The default value of start, stop and steps are 0, length of an object and 1 respectively.
You can also use negative values to change the flow. To understand the negative values, Let’s take an example palindrome-number problem. There are multiple ways to solve this problem. The quick way to solve it is, Convert an integer into a string and reverse the string and compare.

``````class Solution:
def isPalindrome(self, x):
num1 = str(x)
num2 = num1[::-1]
return num1 == num2
print(Solution().isPalindromewithReverse(121)) # True
print(Solution().isPalindromewithReverse(-121)) # False
``````

Here in the above example, The reverse of the string num1 is assigned to num2.

Photo by Brett Jordan on Unsplash

## 3. Memoization

There are cases, Where you need to write recursion code. Memoization can really improve the performance of the code. Memoization is also useful to solve Dynamic Programming coding problems. Memoize can be tricky to implement. However, Using the decorator function it is too easy to implement memoize code. If you want to know more about decorators, You can read my article Validation in Python using Decorator.
To explain this topic, We will solve powx-n problem from Leetcode. In this problem, We have to implement a function that calculates `x` raised to the power `n`.

Code of Memoize Decorator:

``````def join(*args):
return reduce(lambda a, b: f"{a}_{b}", args)
def memoize(func):
memo = {}
def helper(*args):
key = join(*args)
if key not in memo:
memo[key] = func(*args)
return memo[key]
return helper
``````

Now actual solution can be written as below

``````@memoize
def powHelper(x, n):
if n == 0:
return 1
return powHelper(x*x, n/2) if n % 2 == 0 else x * powHelper(x*x, (n-1)/2)
class Solution:
def myPow(self, x, n):
m = n
if n < 0:
m *= -1
res = powHelper(x, m)
return res if n >= 0 else 1/res
print(Solution().myPow(2, 3))  # 8
print(Solution().myPow(2, 5))  # 32
print(Solution().myPow(2, -5))  # 0.03125
``````

## 4. Varargs and Argument Unpacking

To understand Varargs, Let’s take an example of a sum function. The sum is a function, which returns the summation of variable number parameters passed to the function.

``````def sum(*args):
res = 0
for num in args:
res += num
return res
print(sum(1, 2, 3)) # 6
print(sum(1, 2, 3, 4, 5)) # 15
``````

Here sum function can accept variable arguments args. Since args is a tuple is an iterable object(tuple), We can also use reduce from functools. To learn more about functional programming, Read Functional Programming in Python.

``````# Using reduce, simplified version
from functools import reduce
def sum(*args):
return reduce(lambda a, b: a+b, args)
print(sum(1, 2, 3))
print(sum(1, 2, 3, 4, 5))
``````

Note: In the above examples, varargs is a tuple. So we can unpack it.

``````def divide(*args):
num, den = args
return num/den
print(divide(10, 2))
``````

You can also use named varargs. named varargs is a dictionary to accept multiple named arguments. Named varargs is very useful to take option dictionary.

``````def range(**args):
start = args["start"] if "start" in args else 0
end = args["end"] if "end" in args else 100
res = []
while start < end:
res.append(start)
start = start+1
return res
print(range(start=10, end=20)) # [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
``````
Photo by Arseny Togulev on Unsplash

## 5. Mapping of Iterator

Mapping of an iterable object is helpful to convert an iterable object. Mapping can be done using the map function.

``````numbers = [1, 2, 3, 4, 5]
result = map(lambda x: x*x, numbers) # return map object
print(list(result)) # need to convert to list
# Output: [1, 4, 9, 16, 25]
``````

The above example uses a lambda function. If the function has multiple lines of statements, You can use a custom function.

``````numbers = [1, 2, 3, 4, 5]
def mapInt(num):
return num*num if num % 2 == 0 else num*num*num
print(list(map(mapInt, numbers)))
``````

Mapping can be done on any iterable object including sets, lists, tuples etc.

Note: There is a better way to map an iterable object. It is called List comprehensions. List comprehension is a concise and elegant way to map an iterable object.

``````Celsius = [39.2, 36.5, 37.3, 37.8]
Fahrenheit = [((float(9)/5)*x + 32) for x in Celsius]
print(Fahrenheit) # [102.56, 97.7, 99.14, 100.03999999999999]
``````

``````>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
``````

## Conclusion

In this article, I have just mentioned a few tips and patterns. There are a lot of other cool concepts to be learned. To keep the article to a concise length, I have broken this article into multiple sub-article. I will publish those later. I have attached all reference link below.