Wednesday 11 January 2017

NUMPY Queries

1.Import the numpy package under the name np

Import numpy as np


2.Print the numpy version and the configuration

>>> print(np.__version__)
1.11.2
>>> np.show_config()

3.Create a null vector of size 10

>>> Z = np.zeros(10)

>>> Z

4.How to get the documentation of the numpy add function from the command line?

>>>np.info(np.add)

5.Create a null vector of size 10 but the fifth value which is 1

>>>Z = np.zeros(10)
>>>Z[4] = 1
>>>print(Z)

6.Create a vector with values ranging from 10 to 49 

>>>Z = np.arange(10,50)
>>>print(Z)

7.Reverse a vector (first element becomes last)

>>>Z = np.arange(50)

>>>Z = Z[::-1]

8.Create a 3x3 matrix with values ranging from 0 to 8

>>>Z = np.arange(9).reshape(3,3)
>>>print(Z)

9.Find indices of non-zero elements from [1,2,0,0,4,0]

>>>nz = np.nonzero([1,2,0,0,4,0])
>>>print(nz)

10.Create a 3x3 identity matrix

>>>Z = np.eye(3)
>>>print(Z)

or

>>>Z=np.identity(3)

11.Create a 3x3x3 array with random values

>>>X=np.random.random((3,3,3))

12.Create a 10x10 array with random values and find the minimum and maximum values

>>>Z = np.random.random((10,10))
>>>Zmin, Zmax = Z.min(), Z.max()
>>>print(Zmin, Zmax)

13.Create a random vector of size 30 and find the mean value
>>>Z = np.random.random(30)
>>>m = Z.mean()
>>>print(m)

14.Create a 2d array with 1 on the border and 0 inside

Z = np.ones((10,10))
Z[1:-1,1:-1] = 0

15.What is the result of the following expression?

>>> 0 * np.nan
nan
>>> np.nan == np.nan
False
>>> np.inf > np.nan
False
>>> np.nan
nan
>>> np.nan - np.nan
nan
>>> 0.3 == 3 * 0.1
False

16.Create a 5x5 matrix with values 1,2,3,4 just below the diagonal

Z = np.diag(1+np.arange(4),k=-1)
print(Z)

17.Create a 8x8 matrix and fill it with a checkerboard pattern

Z = np.zeros((8,8),dtype=int)
Z[1::2,::2] = 1
Z[::2,1::2] = 1
print(Z)

18.Consider a (6,7,8) shape array, what is the index (x,y,z) of the 100th element?
>>> print(np.unravel_index(100,(6,7,8)))
(1, 5, 4)


19.Create a checkerboard 8x8 matrix using the tile function

>>>Z = np.tile( np.array([[0,1],[1,0]]), (4,4))

20.Normalize a 5x5 random matrix
>>>Z = np.random.random((5,5))
>>>Zmax, Zmin = Z.max(), Z.min()
>>>Z = (Z - Zmin)/(Zmax - Zmin)

>>>print(Z)

21.Create a custom dtype that describes a color as four unisgned bytes (RGBA)
>>> color = np.dtype([("r", np.ubyte, 1),
                  ("g", np.ubyte, 1),
                  ("b", np.ubyte, 1),
                  ("a", np.ubyte, 1)])
>>> color
dtype([('r', 'u1'), ('g', 'u1'), ('b', 'u1'), ('a', 'u1')])

22.Multiply a 5x3 matrix by a 3x2 matrix (real matrix product)

>>>X=np.dot(np.ones((5,3)),np.ones((3,2)))

23.Given a 1D array, negate all elements which are between 3 and 8, in place.
>>> Z = np.arange(11)
>>> Z[(3 < Z) & (Z <= 8)] *= -1
>>> Z
array([ 0,  1,  2,  3, -4, -5, -6, -7, -8,  9, 10])

24.Create a 5x5 matrix with row values ranging from 0 to 4

X=np.ones((5,5))
X+=np.arange(5)

25.




Sunday 8 January 2017

What is pickle in Python?

The pickle module implements binary protocols for serializing and de-serializing a Python object structure. “Pickling” is the process whereby a Python object hierarchy is converted into a byte stream, and “unpickling” is the inverse operation, whereby a byte stream (from a binary file or bytes-like object) is converted back into an object hierarchy. Pickling (and unpickling) is alternatively known as “serialization”, “marshalling,” or “flattening”; however, to avoid confusion, the terms used here are “pickling” and “unpickling”.

Friday 6 January 2017

Excercise and Answers

1) Create an arbitrary one dimensional array called "v".
Ans:>>>import numpy as np
>>>v=np.arange(5)




2) Create a new array which consists of the odd indices of previously created array "v"?
Ans:
>>>import numpy as np
>>>v=np.arange(5)
>>> odd_elements = v[1::2]
>>> odd_elements
array([1, 3, 5, 7, 9])




3) Create a new array in backwards ordering from v
Ans:
>>>import numpy as np
>>>v=np.arange(5)
>>> rv=v[::-1]
>>> rv
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])



4) What will be the output of the following code:

   a = np.array([1, 2, 3, 4, 5])
   b = a[1:4]    #######  array([1, 2, 3, 4, 5])
   b[0] = 200   #######  array([200,   3,   4])
   print(a[1])   ####### 200
>>> a
array([  1, 200,   3,   4,   5])




5) Create a two dimensional array called "m".
Ans:import numpy as np
>>> m=np.arange(10).reshape(2,5)
>>> m.ndim
2
>>> m
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])


6) Create a new array from m, in which the elements of each row are in reverse order.
Ans:

import numpy as np
>>> m=np.arange(10).reshape(2,5)
>>> m.ndim
2
>>> m
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])>>> m[::,::-1]
array([[4, 3, 2, 1, 0],
       [9, 8, 7, 6, 5]])




7) Another one, where the rows are in reverse order.
Ans:import numpy as np
>>> m=np.arange(10).reshape(2,5)
>>> m.ndim
2
>>> m
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
>>>>>> m[::-1]
array([[5, 6, 7, 8, 9],
       [0, 1, 2, 3, 4]])




8) Create an array from m, where columns and rows are in reverse order.
Ans:
import numpy as np
>>> m=np.arange(10).reshape(2,5)
>>> m.ndim
2
>>> m
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])
>>> m[::-1,::-1]
array([[9, 8, 7, 6, 5],
       [4, 3, 2, 1, 0]])



9) Cut of the first and last row and the first and last column?
Ans:
import numpy as np
>>> m=np.arange(10).reshape(2,5)
>>> m.ndim
2
>>> m
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> m[1:-1,1:-1]
array([], shape=(0, 3), dtype=int64)








 


Time Comparison between Python Lists and Numpy Arrays

import numpy as np
from timeit import Timer
size_of_vec = 1000
def pure_python_version():
    X = range(size_of_vec)
    Y = range(size_of_vec)
    Z = []
    for i in range(len(X)):
        Z.append(X[i] + Y[i])
def numpy_version():
    X = np.arange(size_of_vec)
    Y = np.arange(size_of_vec)
    Z = X + Y
#timer_obj = Timer("x = x + 1", "x = 0")
timer_obj1 = Timer("pure_python_version()", "from __main__ import pure_python_version")
timer_obj2 = Timer("numpy_version()", "from __main__ import numpy_version")
print(timer_obj1.timeit(10))
print(timer_obj2.timeit(10))


0.0022348780039465055
6.224898970685899e-05



Thursday 5 January 2017

what is linspace?

The syntax of linspace:
linspace(start, stop, num=50, endpoint=True, retstep=False)


Example:
>>> x=np.linspace(1,10)
>>> x
array([  1.        ,   1.18367347,   1.36734694,   1.55102041,
         1.73469388,   1.91836735,   2.10204082,   2.28571429,
         2.46938776,   2.65306122,   2.83673469,   3.02040816,
         3.20408163,   3.3877551 ,   3.57142857,   3.75510204,
         3.93877551,   4.12244898,   4.30612245,   4.48979592,
         4.67346939,   4.85714286,   5.04081633,   5.2244898 ,
         5.40816327,   5.59183673,   5.7755102 ,   5.95918367,
         6.14285714,   6.32653061,   6.51020408,   6.69387755,
         6.87755102,   7.06122449,   7.24489796,   7.42857143,
         7.6122449 ,   7.79591837,   7.97959184,   8.16326531,
         8.34693878,   8.53061224,   8.71428571,   8.89795918,
         9.08163265,   9.26530612,   9.44897959,   9.63265306,
         9.81632653,  10.        ])

>>> x=np.linspace(1,10,7)
>>> x
array([  1. ,   2.5,   4. ,   5.5,   7. ,   8.5,  10. ])



>>> samples, spacing = np.linspace(1, 10, retstep=True)  
>>> samples
array([  1.        ,   1.18367347,   1.36734694,   1.55102041,
         1.73469388,   1.91836735,   2.10204082,   2.28571429,
         2.46938776,   2.65306122,   2.83673469,   3.02040816,
         3.20408163,   3.3877551 ,   3.57142857,   3.75510204,
         3.93877551,   4.12244898,   4.30612245,   4.48979592,
         4.67346939,   4.85714286,   5.04081633,   5.2244898 ,
         5.40816327,   5.59183673,   5.7755102 ,   5.95918367,
         6.14285714,   6.32653061,   6.51020408,   6.69387755,
         6.87755102,   7.06122449,   7.24489796,   7.42857143,
         7.6122449 ,   7.79591837,   7.97959184,   8.16326531,
         8.34693878,   8.53061224,   8.71428571,   8.89795918,
         9.08163265,   9.26530612,   9.44897959,   9.63265306,
         9.81632653,  10.        ])
>>> spacing
0.1836734693877551


>>> samples, spacing = np.linspace(1, 10, 20, endpoint=True, retstep=True)
>>> samples
array([  1.        ,   1.47368421,   1.94736842,   2.42105263,
         2.89473684,   3.36842105,   3.84210526,   4.31578947,
         4.78947368,   5.26315789,   5.73684211,   6.21052632,
         6.68421053,   7.15789474,   7.63157895,   8.10526316,
         8.57894737,   9.05263158,   9.52631579,  10.        ])
>>> spacing
0.47368421052631576


>>> samples, spacing = np.linspace(1, 10, 20, endpoint=True, retstep=True)
>>> samples
array([  1.        ,   1.47368421,   1.94736842,   2.42105263,
         2.89473684,   3.36842105,   3.84210526,   4.31578947,
         4.78947368,   5.26315789,   5.73684211,   6.21052632,
         6.68421053,   7.15789474,   7.63157895,   8.10526316,
         8.57894737,   9.05263158,   9.52631579,  10.        ])
>>> spacing
0.47368421052631576
>>> samples, spacing = np.linspace(1, 10, 20, endpoint=False, retstep=True)
>>> samples
array([ 1.  ,  1.45,  1.9 ,  2.35,  2.8 ,  3.25,  3.7 ,  4.15,  4.6 ,
        5.05,  5.5 ,  5.95,  6.4 ,  6.85,  7.3 ,  7.75,  8.2 ,  8.65,
        9.1 ,  9.55])
>>> spacing
0.45
>>> 






Difference between range and arange?

1.

>>> x=np.arange(1,10)
>>> x
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> print(x)
[1 2 3 4 5 6 7 8 9]        #No commas
>>> type(x)
<type 'numpy.ndarray'>


>>> x=range(1,10)
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(x)
[1, 2, 3, 4, 5, 6, 7, 8, 9]  # is separated by commas
>>> type(x)
<type 'list'>


2.
>>> x=np.arange(10.5)  # Floating point value
>>> x
array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.])
>>> print(x)
[  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.]

>>> x=range(10.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: range() integer end argument expected, got float.



3.









What is arange?

There are functions provided by Numpy to create evenly spaced values within a given interval. One uses a given distance 'arange' and the other one 'linspace' needs the number of elements and creates the distance automatically.


The syntax of arange:
arange([start,] stop[, step,], dtype=None)

It is nearly equivalent to the Python built-in function "range", but arange returns an ndarray rather than a list iterator as range does. 

Wednesday 4 January 2017

How to import data with genfromtxt?

Numpy provides several functions to create arrays from tabular data.
genfromtxt runs two main loops:
1.The first loop converts each line of the file in a sequence of strings.
2.The second loop converts each string to the appropriate data type.

Syntax:
numpy.genfromtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None, skip_header=0, skip_footer=0, converters=None, missing_values=None, filling_values=None, usecols=None, names=None, excludelist=None, deletechars=None, replace_space='_', autostrip=False, case_sensitive=True, defaultfmt='f%i', unpack=None, usemask=False, loose=True, invalid_raise=True, max_rows=None)

This mechanism is slower than a single loop, but it gives more flexibility. In particular, "genfromtxt" is able to take missing data into account, when other faster and simpler functions like "loadtxt" cannot.

Process:
1.Defining the Input:
The input file can be a text file or an archive

2.Splitting the lines into columns:
The delimiter argument
Example:
>>> data = "1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(StringIO(data), delimiter=',')
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])


>>> data = "  1  2  3\n  4  5 67\n890123  4"
>>> np.genfromtxt(StringIO(data), delimiter=3)
array([[   1.,    2.,    3.],
       [   4.,    5.,   67.],
       [ 890.,  123.,    4.]])


>>> data = "123456789\n   4  7 9\n   4567 9"
>>> np.genfromtxt(StringIO(data), delimiter=(4, 3, 2))
array([[ 1234.,   567.,    89.],
       [    4.,     7.,     9.],
       [    4.,   567.,     9.]])


The autostrip argument

>>> data = "1, abc , 2\n 3, xxx, 4"
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|S5")
array([['1', ' abc ', ' 2'],
       ['3', ' xxx', ' 4']], 
      dtype='|S5')
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|S5", autostrip=True)
array([['1', 'abc', '2'],
       ['3', 'xxx', '4']], 
      dtype='|S5')


The comments argument
>> data = """#
... # Skip me!
... #Skip me too!
... 1,2
... 3,4
... 5,6
... 7,8
... #And here comes the last line
... 9,0
... """
>>> np.genfromtxt(StringIO(data), comments="#", delimiter=",")
array([[ 1.,  2.],
       [ 3.,  4.],
       [ 5.,  6.],
       [ 7.,  8.],
       [ 9.,  0.]])


Skipping lines and choosing columns
The skip_header and skip_footer arguments
The presence of a header in the file can hinder data processing. In that case, we need to use the skip_header optional argument. The values of this argument must be an integer which corresponds to the number of lines to skip at the beginning of the file, before any other action is performed. Similarly, we can skip the last n lines of the file by using the skip_footer attribute and giving it a value of n:

 data = "\n".join(str(i) for i in range(10))
>>> np.genfromtxt(StringIO(data),)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> np.genfromtxt(StringIO(data),
...              skip_header=3, skip_footer=5)
array([ 3.,  4.])


The usecols argument:
In some cases, we are not interested in all the columns of the data but only a few of them. We can select which columns to import with the usecols argument. This argument accepts a single integer or a sequence of integers corresponding to the indices of the columns to import. Remember that by convention, the first column has an index of 0. Negative integers behave the same as regular Python negative indexes.

>>> data = "1 2 3\n4 5 6"
>>> data
'1 2 3\n4 5 6'
>>> np.genfromtxt(StringIO(data), usecols=(0, -1))
array([[ 1.,  3.],
       [ 4.,  6.]])

If the columns have names, we can also select which columns to import by giving their name to the usecols argument, either as a sequence of strings or a comma-separated string:

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data),names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)], 
      dtype=[('a', '<f8'), ('c', '<f8')])



Setting the names
The names argument

>>> data = StringIO("1 2 3\n 4 5 6")
>>> data 
<StringIO.StringIO instance at 0x7f38ac086680>
>>> np.genfromtxt(data, dtype=[(_, int) for _ in "abc"])
array([(1, 2, 3), (4, 5, 6)], 
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])


>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)], 
      dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])


>>> data = StringIO("So it goes\n#a b c\n1 2 3\n 4 5 6")
>>> np.genfromtxt(data, skip_header=1, names=True)
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)], 
      dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])


The defaultfmt argument
If names=None but a structured dtype is expected, names are defined with the standard NumPy default of "f%i", yielding names like f0, f1 and so forth:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2.0, 3), (4, 5.0, 6)], 
      dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<i8')])

>>> data = StringIO("1 2 3\n 4 5 6")

>> data
<StringIO.StringIO instance at 0x7f38a709fc20>
>>> np.genfromtxt(data, dtype=(int, float, int), names="a")
array([(1, 2.0, 3), (4, 5.0, 6)], 
      dtype=[('a', '<i8'), ('f0', '<f8'), ('f1', '<i8')])




How do we create a Numpy array?

Numpy has built-in functions for creating arrays.

zeros(shape) will create an array filled with "0" values with the specified shape. The default dtype is float64.
Example:
>>> np.zeros((2, 3))
array([[ 0.,  0.,  0.],

       [ 0.,  0.,  0.]])

ones(shape) will create an array filled with 1 values.
Example:
>>> np.ones((2,3))
array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])

arange() will create arrays with regular increment values
Example:
>>> np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> np.arange(2, 10, dtype=np.float)
array([ 2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])


>>> np.arange(2, 3, 0.1)
array([ 2. ,  2.1,  2.2,  2.3,  2.4,  2.5,  2.6,  2.7,  2.8,  2.9])

linspace() will create arrays with a specified number of elements, and spaced equally between the specified beginning and end values.
Example:
>>> np.linspace(1., 4., 6)
array([ 1. ,  1.6,  2.2,  2.8,  3.4,  4. ])

indices() will create a set of arrays (stacked as a one-higher dimensioned array), one per dimension with each representing variation in that dimension.
Example:
>>> np.indices((3,3))
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]],

       [[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2]]])







How to convert Python array_like Objects to Numpy Arrays?

In general, numerical data arranged in an array-like structure(example: lists and tuples.) in Python can be converted to arrays by the use of the array() function.

Example:
>>> x = np.array([2,3,1,0])
>>> x
array([2, 3, 1, 0])

>>> x = np.array([[1,2.0],[0,0],(1+1j,3.)])
>>> x
array([[ 1.+0.j,  2.+0.j],
       [ 0.+0.j,  0.+0.j],
       [ 1.+1.j,  3.+0.j]])



>>> x = np.array([[ 1.+0.j, 2.+0.j], [ 0.+0.j, 0.+0.j], [ 1.+1.j, 3.+0.j]])
>>> x
array([[ 1.+0.j,  2.+0.j],
       [ 0.+0.j,  0.+0.j],
       [ 1.+1.j,  3.+0.j]])

Monday 2 January 2017

Sort a python Dictionary by Value

You cannot  sort a dictionary, but only get a representation of a dictionary that is sorted. Dictionary are inherently order-less, but other types such as lists and tuples, are not. So you need a sorted representation, which will be a list—probably a list of tuples.

Sort a python Dictionary by value:

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))

Sort a python Dictionary by Key:
import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))

What is Bubble sort?

 It compares adjacent items and exchanges those that are out of order.

Example:
x=[47, 98, 85, 85, 67, 67]
def bubble_sort(x):
for i in range(len(x)-1,0,-1):
for j in range(i):
if x[j]>x[j+1]:
temp=x[j]
x[j]=x[j+1]
x[j+1]=temp
return x

>>> print(bubble_sort(x))
[47, 67, 67, 85, 85, 98]

Insertion Sort in Python

Insertion sort is a simple sorting algorithm that builds the final sorted  list one item at a time. It is much less efficient.

Best-case performance: O(n) comparisons,
Average performance:О(n2) comparisons
Worst-case space complexity:О(n) total

Example:
x=[98,67,85,67,85,47]
>>> def insertion_sort(x):
for i in range(1,len(x)):
j=i
while j>0 and x[j]<x[j-1]:
x[j],x[j-1]=x[j-1],x[j]
j=j-1
return k

>>> insertion_sort(x)

[6, 7, 12, 32, 43, 45]

Sunday 1 January 2017

Quicksort in Python

Quicksort is divide and conquer sorting algorithm.

Working:
A quick sort first selects a value, which is called the pivot value.
The role of the pivot value is to assist with splitting the list.
The actual position where the pivot value belongs in the final sorted list, commonly called the split point, will be used to divide the list for subsequent calls to the quick sort.
Expected Running time:O(nlogn)
Worstcase :O(n^2)

Advantages:
1.Good Cache Performance
2.Good Parallel Performance


Quick sort has two phase:
1.Partition Phase
def partition(myList, start, end):
    pivot = myList[start]
    left = start+1
    right = end
    done = False
    while not done:
        while left <= right and myList[left] <= pivot:
            left = left + 1
        while myList[right] >= pivot and right >=left:
            right = right -1
        if right < left:
            done= True
        else:
            # swap places
            temp=myList[left]
            myList[left]=myList[right]
            myList[right]=temp
    # swap start with myList[right]
    temp=myList[start]
    myList[start]=myList[right]
    myList[right]=temp
    return right

2.Sort Phase
def quicksort(myList, start, end):
    if start < end:
        # partition the list
        pivot = partition(myList, start, end)
        # sort both halves
        quicksort(myList, start, pivot-1)
        quicksort(myList, pivot+1, end)
    return myList

what is sorting algorithm?

An algorithm that puts elements of list in an order.The most used orders are :
1.Numerical Order
2.Lexicographical order

Sorting algorithms are classified by:
1.Computational Complexity(Worst,Average,Best)
2.Memory Usage
3.Recursion or Non Recursive
4.Stability
5.Adaptibility

what is Numpy?

Numpy is fundamental package for scientific computing.
At the core of Numpy package we have ndarray object.It encapsulates n-dimensional arrays of homogeneous data types, with many operations being performed in compiled code for performance.

Numpy numerical types are instances of dtype (data-type) objects, each having unique characteristics. Once you have imported Numpy using
>>> import numpy as np



There are many differences between Numpy Arrays and python sequences:
1.Numpy arrays have a fixed size at creation, unlike Python lists (which can grow dynamically). Changing the size of an nd-array will create a new array and delete the original.



What is a class in Python?


A class is a special kind of objects which creates new objects called instances.
You can change classes by using two different techniques:

1.monkey patching
2.class decorators
3.Metaclasses