Python Interview Questions on HTML/XML in Python

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on HTML/XML in Python

Question 1:
What module is used to handle URLs?
Answer:
urlparse.

Question 2:
Illustrate parsing a URL into a tuple.
Answer:
Import urlparse
myTuple = urlparse.urlparse( “http://wivzv.example.com/mydirectory/ myfile.html”)

Question 3:
What modules does Python provide for opening and fetching data from URLs?
Answer:
urllib and urllib2.

Question 4:
What is the main difference between the urllib and urllib2 modules?
Answer:
The urllib2 module can accept a Request object, which allows the specification of headers.

Question 5:
Illustrate opening and printing a web-based URL.
Answer:
import urllib ,
myURL =urllib.urlopen(“http:/lwww.example.org/myfile.html”)
myBuffer = myURL.read( )
print myBuffer

Question 6:
How is the requested information retrieved?
Answer:
Using the .info( ) method on an open URL.

Question 7:
To process the contents of an HTML file, what module is used?
Answer:
HTMLParser.

Question 8:
How is the HTMLParser used?
Answer:
By subclassing the HTMLParser.HTMLParser class, and inserting processing for the tags of interest, instantiating it, then by calling the .feed method of the resulting object.

Question 9:
What modules are used to manage cookies?
Answer:
The urllib and cookielib modules.

Question 10:
Illustrate retrieving a cookies from a URL.
Answer:
Import urllib2
Import cookielib
myjar= cookielib.LWPCookieJar( )
myOpener = urllib2.build_opener(
urllib2.HTTPCookieProcessor(myJar))
urllib2. ins tall_opener( myOpener)
myRequest =urllib2. RequestC’http:!/www.example.org/”)
myHTML = urllib2.urlopen(my Request)
for cookie in enumerate(myjar)
print cookie

Question 11:
In managing XML documents, what is the module most often used?
Answer:
xml.dom Also, the minidom class within xml.dom

Question 12:
Illustrate opening an xml document.
Answer:
from xml.dom import minidom myTree = minidom.parse(‘myXML.xml’) print myTree.toxmlO

Question 13:
What tools are available to determine if an xml file is well formed?
Answer:
Using a try, except block, and attempting to parse the xml file through the generic parser available in xml.sax

Question 14:
How are xml element attributes accessed?
Answer:
Using the minidom parser, then the .getAttribute method on returned child nodes.

Question 15:
What method is used to determine if a child node includes a particular attribute?
Answer:
Using the .hasAttribute method which will return true if the attribute is defined.

Question 16:
What is the difference between the .toxml and .toprettyxml methods?
Answer:
The .toprettyxml method will indent the node contents appropriately.

Question 17:
What is expat module and what is it used for?
Answer:
The expat module is a non-validating XML parser. It is used to process XML very quickly, with minimal regard for the formal correctness of the XML being parsed.

Question 18:
What does the .dom stand for in the xml.dom module?
Answer:
Document Object Model.

Question 19:
What parsers are available for XML?
Answer:
Sax, expat, and minidom

Question 20:
Illustrate direct node access in an XML file.
Answer:
from xml.dom import minidom myXML = minidom.parse(”myXML.xml”)
myNodes = myXML.childNode s print childNodes[0].toprettyxml()

Python Interview Questions on Trees

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on Trees

There are several types of data structures that can be used to tackle application problems. We have seen how linked lists work in a sequential manner, we have also seen how stacks and queues can be used in programming applications but they are very restricted data structures. The biggest problem while dealing with linear data structure is that if we have to conduct a search, the time taken increases linearly with the size of the data. Whereas there are some cases where linear structures can be really helpful but the fact remains that they may not be a good choice for situations that require high speed.

So, now let’s move on from the concept of linear data structures to nonlinear data structures called trees. Every tree has a distinguished node called the root. Unlike the trees that we know in real life the tree data structure branches downwards from parent to child and every node except the root is connected by a direct edge from exactly one other node.

Python Interview Questions on Trees chapter 13 img 1

Look at the figure given above:
A is the root and it is parent to three nodes – B, C, and D.
Same way B is a parent to E and C is a parent to F and G.

Nodes like D, E, F, and G that have no children are called leaves or external nodes.
Nodes that have at least child such as B and C are called internal nodes.
The number of edges from the root to the node is called the depth or level of a
node. The depth of B is 1 whereas the depth of G is 2.
The height of a node is the number of edges from the node to the deepest leaf.
B, C, and D are siblings because they have the same parent A. Similarly, F and
G are also siblings because they have the same parent C.
Children of one node are independent of children of another node.
Every leaf node is unique.

  • The file system that we use on our computer machines is an example of the tree structure.
  • Additional information about the node is known as payload. Payload is not given much importance in algorithms but it plays a very important role in modem day computer applications.
  • An edge connects two nodes to show that there is a relationship between them.
  • There is only one incoming edge to every node (except the root). However, a node may have several outgoing edges.
  • The root of the tree is the only node of the tree that has no incoming edges as it marks the starting point for a tree.
  • Set of nodes having incoming edges from the same node are the children of that node.
  • A node is a parent of all the nodes that connect to it by the outgoing edges.
  • A set of nodes and edges that comprises a parent along with all its descendants are called subtrees.
  • A unique path traverses from the root to each node.
  • A tree that has a maximum of two children is called a binary tree.

Recursive definition of a tree

A tree can be empty, or it may have a root with zero or more subtree. The root of every subtree is connected to the root of a parent tree by an edge.

Simple Tree Representation

Have a look at the following tree. Here, we are considering the case of a binary tree.

Python Interview Questions on Trees chapter 13 img 2

In the case of a binary tree, a node cannot have more than two children. So, for ease of understanding, we can say that the above scenario is similar to something like this:

Python Interview Questions on Trees chapter 13 img 3

In this diagram, left and right are references to the instances of the node that are located on the left and right of this node respectively. As you can see, every node has three values:

  1. Data
  2. Reference to the child on left
  3. Reference to the child on the right

Python Interview Questions on Trees chapter 13 img 4

So, the constructor for the node class to create a Node object will be as follows:

class Node(object):
     def_init_(self, data_value):
         self.data_value = data_value
         self.left = None
         self.right = None

So, this is how a root node is created:

# Root_Node
print ("Create Root Node")
root = Node("Root_Node")
print("Value of Root = ",root.data_value," left =",root.left, " right = ", root.right)

When we execute this block of code, the output is as follows:
Create Root Node

Value of Root = Root_Node left = None right = None
Value of Node = Tree Left left = None right = None

Now, we write code for inserting values to the left or right. When a node is created, initially it’s left and right reference point to None. So, to add a child to left we just need to say:
self. left = child_node
and a child can be added to right in a similar way:
self.left = child_node
However, if the root node is already pointing to some existing child and we try to insert a child node then the existing child should be pushed down one level and the new object must take its place. So, the reference of the existing child stored in self.left is passed on to child.left and then self.help is assigned the reference to child. This can be achieved in the following manner:

def insert_left (self, child):
     if self.left is None :
        self.left = child
else:
     child.left = self.left
     self.left = child
def insert_right(self, child):
    if self.right is None:
       self.right = child
else :
    child.right = self.right
    self.right = child

Code

class Node(object):
     def_init_(self, data__value) :
        self . data_value = data_value
        self.left = None
        self.right = None

def insert_left(self, child):
     if self.left is None:
         self.left = child
else:
     child.left = self.left
     self.left = child
def insert_right(self, child):
    if self.right is None:
       self.right = child
else:
    child.right = self.right
    self.right = child

Execution

# Root_Node
print ("Create Root Node")
root = Node("Root_Node")
print("Value of Root = ",root.data_value," left = ",root.left, " right = ", root.right)
#Tree_Left
print("Create Tree_Left")
tree_left = Node("Tree_Left")
root.insert_left(tree_left)
print ("Value of Node = ", tree__left. data_value, " left =",tree_left.left, " right = ",tree_left. right)
print("Value of Root = ", root,data_value," left = ",root.left, " right = ", root.right)
#Tree_Right
print("Create Tree_Right")
tree__right = Node ("Tree_Right")
root.insert_right(tree_right)
print("Value of Node = ",tree_right.data_value, " left =",tree_right.left, " right = ",tree_right. right)
print("Value of Root = " , root. data__value, " left = ",root.left, " right = ", root.right)
#TreeLL
print("Create TreeLL") tree11 = Node("TreeLL")
tree_left.insert_left(tree11)
print("Value of Node = ",tree11.data_value," left =",tree11.left, " right = ",tree11.right)
print("Value of Node = ",tree_left.data_value," left =",tree_left.left, " right = ",tree_left. right)
print("Value of Root = ", root.data_value," left =",root.left, " right = ", root.right)

Output

Create Root Node
Value of Root = Root_Node left = None right = None
Create Tree_Left
Value of Node = Tree_Left left = None right = None
Value of Root = Root_Node left = < main .Node
object at 0x000000479EC84F60> right = None Create Tree_Right
Value of Node = Tree_Right left = None right = None
Value of Root = Root_Node left = < main .Node
object at 0x000000479EC84F60> right = < main
Node object at 0x000000479ED05E80>
Create TreeLL
Value of Node = TreeLL left = None right =
None
Value of Node = Tree_Left left = < main .Node
object at 0x000000479ED0F160> right = None
Value of Root = Root_Node left = < main .Node
object at 0x000000479EC84F60> right = < main .
Node object at 0x000000479ED05E80>

Question 1.
What is the definition of a tree?
Answer.
A tree is a set of nodes that store elements. The nodes have a parent-child relationship such that:

  • If the tree is not empty, it has a special node called the root of the tree. The root has no parents.
  • Every node of the tree different from the root has a unique parent node.

Question 2.
Write a code to represent a tree through lists or a list of lists.
Answer:
In the list of lists, we shall store the value of the node as the first element. The second element will be the list that represents the left subtree and the third element represents the right subtree. The following figure shows a tree with just the root node.

Python Interview Questions on Trees chapter 13 img 5
Now, suppose we add a node to the left.
Python Interview Questions on Trees chapter 13 img 6
Now, adding another subtree to the right would be equal to:
Python Interview Questions on Trees chapter 13 img 7
Same way adding a node to the left of Tree Left would mean:

Implement Trees with Lists

Python Interview Questions on Trees chapter 13 img 8

Here you can see that the tree can be defined as follows:
binary_tree = [ ‘Root_Node’ , [ ‘Tree_Left’ ,[ ‘TreeLL’,[ ],[ ] ],[ ] ], [ ‘Tree_Right’,[ ],[ ] ] ]

Here, the root is Root_Node which is located at binary_tree[0].
Left subtree is at binary_tree[1].
The right subtree is at binary_tree[2].
Now let’s write the code for this.

Step 1:

Define Class

class Tree:

Step 2:

Create Constructor

Now let’s write the code for the constructor. Here, when we create an object we pass a value. The constructor creates a list where the value is placed at index 0 and at index 1 and 2 we have two empty lists. If we have to add subtree at the left side we will do so at index 1 and for the right subtree, we will insert values in the list at index 2.

def_init_(self, data):
self.tree = [data, [ ],[ ] ]

Step 3:

Define a function to insert left and right subtree

If you have to insert a value in the left subtree then pop the element at index 1 and insert the new list at that place. Similarly, if you have to insert a child on the right-hand side, pop the value at index 2 and insert the new list.

def left_subtree(self,branch):
        left_list = self . tree.pop(1)
        self.tree.insert(1,branch.tree)

def right_subtree(self,branch):
    right_list = self.tree.pop(2)
    self.tree.insert(2,branch.tree)

Now, let’s execute the code:

Code

class Tree:
     def_init_(self,data):
         self.tree = [data, [ ],[ ] ]

def left_subtree(self,branch):
    left_list = self.tree.pop(1)
    self.tree.insert(1,branch.tree)

def right_subtree(self,branch):
    right_list = self.tree.pop(2)
     self.tree.insert(2,branch.tree)

Execution

print("Create Root Node")
root = Tree("Root_node")
print("Value of Root = ", root.tree)
print("Create Left Tree")
tree_left = Tree("Tree_Left")
root.left_subtree(tree_left)
print("Value of Tree_Left = ", root.tree)
print("Create Right Tree")
tree_right = Tree("Tree_Right")
root.right_subtree(tree_right)
print("Value of Tree_Right = ", root.tree)
Create Root Node
Value of Root = ['Root_node', [ ], [ ] ]
Create Left Tree
Value of Tree_Left = ['Root_node', ['Tree_Left', [ ] ]/ [ ] ]
Create Right Tree
Value of Tree_Right = ['Root_node', [ 'Tree_Left', [ ], [ ] ], ['Tree_Right', [ ], [ ] ] ]
Create Terrell
Value of Tree_Left = ['Root_node', ['Tree_Left', [ 'TreeLL', [ ], [ ] ], [ ] ], ['Tree_Right', [ ], [ ] ] ]
>>>

There is however one thing ignored in this code. What if we want to insert a child somewhere in between? Here, in this case the child will be inserted at the given location and the subtree existing at that place will be pushed down.

For this we make changes in the insert functions.

def left_subtree(self,branch):
    left_list = self.tree.pop(1)
    if len(left_list) > 1:
        branch.tree[1]=left_list
        self.tree.insert (1,branch.tree)
  else:
        self.tree.insert(1,branch.tree)

If we have to insert a child in the left then first we pop the element at index 1. If the length of element at index 1 is 0 then, we simply insert the list. However, if the length is not zero then we push the element to the left of the new child. The same happens in case of right subtree.

def right_subtree(self,branch):
       right_list = self.tree.pop (2)
       if len(right_list) > 1:
          branch.tree[2]=right_list
          self.tree.insert(2,branch.tree)
    else:
         self.tree.insert(2,branch.tree)
print ("Create TreeLL")
tree11 = Tree("TreeLL")
tree_left.left_subtree(tree11)
print("Value of Tree_Left = ", root.tree)

Code

class Tree:
   def_init_(self,data) :
      self.tree = [data, [ ], [ ] ]

def left_subtree(self,branch):
    left_list = self.tree.pop(1)
    if len(left_list) > 1:
       branch.tree[1]=left_list
       self.tree.insert (1,branch.tree)
  else:
       self.tree.insert(1,branch.tree)

def right_subtree(self,branch):
    right_list = self.tree.pop(2)
    if len(right_list) > 1:
        branch.tree[2]=right_list
        self.tree.insert(2,branch.tree)
   else:
      self.tree.insert(2,branch.tree)

Execution

print ("Create Root Node")
root = Tree("Root_node")
print("Value of Root = ", root.tree)
print("Create Left Tree")
tree_left = Tree("Tree_Left")
root.left_subtree(tree_left)
print("Value of Tree_Left = ", root.tree)
print("Create Right Tree")
tree_right = Tree("Tree_Right")
root.right_subtree(tree_right)
print("Value of Tree_Right = ", root.tree)
print("Create Left Inbetween")
tree_inbtw = Tree("Tree left in between")
root.left_subtree(tree_inbtw)
print("Value of Tree_Left = ", root.tree)
print("Create TreeLL")
treell = Tree("TreeLL")
tree_left.left_subtree(tree11)
print("Value of TREE = ", root.tree)

Output

Create Root Node
Value of Root = ['Root node', [ ], [ ] ]
Create Left Tree
Value of Tree Left = [ 'Root node', [ 'Tree Left', [ ], [ ] ], [ ] ]
Create Right Tree
Value of Tree Right = [ 'Root node' , [ 'Tree Left', [ ] , [lie 1 'Tree Right', [], [ ] ] ]
Create Left Inbetween
Value of Tree Left = [ 'Root node' , [ 'Tree left in between' , [ 'Tree Left', [ ] , [ ] ] , [ ] ] ,
['Tree_Right', [ ], [ ] ] ]
Create TreeLL
Value of TREE = [ 'Root node', [ 'Tree left in between' , ['Tree Left' , [ 'TreeLL', [ ], [ ] ], [ ]], ['Tree_Right', [ ], [ ] ] ]
>>>

Question 3.
What is a binary tree? What are the properties of a binary tree?
Answer:
A data structure is said to be a binary tree if each of its nodes can have at most two children. The children of the binary tree are referred to as the left child and the right child.

Question 4.
What are tree traversal methods?
Answer:
Traversals
Preorder Traversal: First visit the root node, then visit all nodes on the left followed by all nodes on the right.

Code

class Node(object) :
     def_init_(self, data_value):
         self.data_value = data_value
         self.left = None
         self.right = None
def insert_left(self, child):
     if self.left is None:
        self.left = child
else:
      child.left = self.left
      self.left = child
def insert_right(self, child):
      if self.right is None:
         self.right = child
else:
     child.right = self.right
     self.right = child
def preorder(self, node):

res= [ ]
if node:
      res.append(node.data_value)
      res = res + self.preorder(node.left)
      res = res + self.preorder(node.right) 
   return res

Execution

# Root_Node
print("Create Root Node")
root = Node("Root_Node")
#Tree_Left
print ("Create Tree_Left")
tree_left = Node("Tree_Left")
root.insert_left(tree_left)
#Tree_Right
print("Create Tree_Right")
tree_right = Node("Tree_Right")
root.insert_right(tree_right)
#TreeLL
print ("Create TreeLL")
treell = Node("TreeLL")
tree_left.insert_left(treell)
print("*****Preorder Traversal*****")
print(root.preorder(root))

Output

Create Root Node
Create Tree_Left
Create Tree_Right
Create TreeLL
*****Preorder Traversal*****
['Root_Node', 'Tree_Left', 'TreeLL', 'Tree_ Right']
>>>

In Order Traversal :First visit all nodes on the left then the root node and then all nodes on the right..

class Node(object):
     def_init_(self, data_value):
          self.data_value = data_value
          self.left = None
          self.right = None
   def insert_left(self, child):
       if self.left is None:
          self.left = child
   else:
      child.left = self.left
       self.left = child
def insert_right(self, child):
    if self.right is None:
       self.right = child
else:
     child.right = self.right
     self.right = child
def inorder(self, node):
  res=[ ]
  if node:
       res = self.inorder(node.left)
       res.append(node.data_value)
        res = res + self.inorder(node.right)
  return res
# Root_Node
print("Create Root Node")
root = Node("Root_Node")
#Tree_Left
print ("Create Tree_Left")
tree_left = Node("Tree_Left")
root.insert_left(tree_left)
#Tree_Right
print("Create Tree_Right")
tree_right = Node("Tree_Right")
root.insert_right(tree_right)
#TreeLL
print("Create TreeLL")
treell = Node ("TreeLL")
tree_left.insert_left (treell)
print("*****Inorder Traversal*****)
print(root.inorder(root))

Output

Create Root Node
Create Tree_Left
Create Tree_Right
Create TreeLL
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
*****Inorder Traversal*****
["TreeLL' , 'Tree_Left' , 'Root_Node' , 'Tree_Right']
>>>

Post order Traversal: Visit all nodes on the left, then visit all nodes on the right , then visit the root node.

code

class Node(object):
     def_init_(self, data_value):
        self.data_value = data_value
        self.left = None
        self.right = None
    def insert_left (self, child):
        if self.left is None:
           self.left = child
else:
     child.right = self.right
     self.right = child
def postorder (self, node):
res = [ ]
if node:
    res = self.postorder (node.left)
    res = res + self.postorder (node.right)
    res.append (node.data_value)

   return res

Execution

#Root_Node
print("Create Root Node")
root = Node ("Root_Node")
#Tree_Left
print("Create Tree_Left")
tree_left = Node("Tree_Left")
root.insert_left(tree_left)
#Tree_Right
print("Create Tree_Right")
tree_right = Node("Tree_Right")

root . insert_right(tree_right)
#TreeLL
print ("Create TreeLL")
tree11 = Node ("TreeLL")
tree_left . insert_left(tree11)
print("*****postorder Traversal *****")
print (root.postorder (root))
OUTPUT
Create Root Node
Create Tree_Left
Create Tree_Right
Create TreeLL
*****Postorder Traversal*****
['TreeLL' , 'Tree_Left' , 'Tree_Right' , "Root_Node']

A binary heap is a binary tree. It is a complete tree, which means that all levels are completely filled except possibly the last level. It also means that the tree is balanced
As every new item is inserted next to the available space. A Binary heap can be stored in an array.

A binary heap can be of two types: Min Heap or Max Heap. In case of Min Heap binary heap, the root node is the minimum of all the nodes in the binary heap,all parent nodes are smaller than their children. On the other hand in case of Max Heap the root is the maximum among all the keys present in the heap and and all nodes are bigger than their children.

 

Python Interview Questions on Trees chapter 13 img 9

Heap has two important properties:
1. It is complete and is constructed from left to right across each row and the last row may not be completely full. Each row should be filled
up sequentially. So, the order of inserting values should be from left to right, row by row sequentially as shown in the following figure:

 

Python Interview Questions on Trees chapter 13 img 10

2. The parent must be larger (Max Heap)/smaller(Min Heap) than the children.

 

Python Interview Questions on Trees chapter 13 img 11

Look at the figure shown above, it is a case of Maximum heap because all parents are greater than their children.
The binary heap can be represented in an array as follows:
0 1 2 3 4 5

 

Python Interview Questions on Trees chapter 13 img 12

If you look at the array carefully you will realize that if a parent exists at location n then the left child is at 2n+l and the right child is at 2n + 2. ”
n= 0

 

Python Interview Questions on Trees chapter 13 img 13

So, if we know the location of the parent child we can easily find out the location of the left and right child.
Now suppose we have to build up a Max Heap using following values: 20,4, 90, 1, 125

Step 1:
Insert 20

 

Python Interview Questions on Trees chapter 13 img 14

Step 2:
Insert 4 -> Left to Right-> First element in second row

 

Python Interview Questions on Trees chapter 13 img 15

Since the parent is greater than the child, this is fine.

Step 3:
Insert 90 -> Left to Right -> Second element in second row
However when we insert 90 as the right child, it violates the rule of the max heap because the parent has a key value of 20. So, in order to resolve this problem it is better to swap places as shown in the following figure:

 

Python Interview Questions on Trees chapter 13 img 16

After swapping, the heap would look something like this:

 

Python Interview Questions on Trees chapter 13 img 17

Step 4:
Insert 1 -> left to right -> first element third row

 

Python Interview Questions on Trees chapter 13 img 18

Since 1 is smaller than the parent node, this is fine.
Step 5:
Insert 125

 

Python Interview Questions on Trees chapter 13 img 19

This violated the rule of Max Heap Swap 4 and 125

 

Python Interview Questions on Trees chapter 13 img 20

Not a heap still Swap 125 and 90

 

Python Interview Questions on Trees chapter 13 img 21

We now have a Max Heap.

Question 5.
Write python code to implement Max Heap. How would you insert a value so that the root node has the maximum value and all parents are greater than their children.
Answer:
Here we will write The code to implement Maxheap class. The class will have two functions:
Push( ) : To insert value.
FIoat_up( ): To place the value where it belongs.

Step 1

Define the class

class MaxHeap:

Step 2

Define The Constructor

def init (self):
self.heap = [ ]

Step 3

Define the push( ) function

The push function does two jobs:

  1. Appends the value at the end of the list. (We have seen earlier that the value has to be inserted first at the available location)
  2. After appending the value, the push() function will call the float_ up(index) function. The pushQ function passes the index of the last element to the float_up() function so that the float_up() function can analyse the values and do the needful as described in the next step.
def push(self,value):
                 self.heap.append(value)
                 self .floatj_up (len (self . heap) -1)

Step 4
Define the float_up function
1. The float_up( )function takes index value as parameter.

def float_up (self, index) :

2. The push( ) function passes the index value of the last element in the heap. The first thing that the float function does is that it checks if the element is at index 0 or not. If yes, then it is the root node. Root node has no parents and since it’s the last element of the heap it has no children either. So, we can return the value.

if index==0:
return

3. If the index is greater than 0 then we can proceed further. Look the following figure once again:

 

Python Interview Questions on Trees chapter 13 img 22

For programming, you need to understand few things:

The index 0 has two children at position 1 and 2. If I have an element at 1 then I can find the parent by calculating value of (1//2). Similarly for element at index 2, the parent’s index value can be found out by calculating value of (2//2 -1). So, we can say that if an element has an index value index and it is odd then it’s parent’s index value, parent_ ofindex = index//2. However, if index is even parentofindex will be (index//2)-1. This becomes the outer frame for the float_up function. The right child has an even number as index and the left child has an odd number as index.

if index==0:
         return
   else:
      if index%2==0:
         parent_of_index = (index//2)-l
         ----------write code----------
  else:
         parent_of_index = index//2
         ----------write code--------

4. Now, we compare the values of the child and the parent. If the child is greater than the parent, swap the elements.

def float_up (self, index) :
        if index==0:
              return
      else:
        if index%2==0:
           parent_of_index = (index//2)-1
           if self.heap[index]> self, heap[parent_of_index]:
          self.swap(index, parent_of_index)
     else:
          parent_of_index = index//2
          if self.heap[index]> self, heap[parent_of_index]:
         self.swap(index, parent_of_index)
         self.float_up (parent_of_index)

Step 5

Define the swap function

The swap( ) function helps in swapping the values of the parent and the child, the code for this is given as follows:

def swap(self,indexl, index2):
      temp = self.heap[index1]
      self.heap[index1] = self.heap[index2]
      self.heap[index2] = temp

Code

class MaxHeap:
      def_init_(self):
          self.heap = [ ]
     def push(self,value):
          self.heap.append(value)
          self .float_up (len (self .heap) -1)
    def float_up (self, index) :
         if index==0:
 return
    else:
            if index%2==0:
             parent_of_index = (index//2)-1
             if self.heap[index]> self, heap[parent_of_index]:
          self.swap(index, parent_of_ index)
  else:
            parent_of_index = index//2
            if self.heap[index]> self, heap[parent_of_index]:
            self.swap(index, parent_of_index)
           self .float_up (parent_of_index)
def peek(self):
      print(self.heap[0])
def pop(self):
        if len(self.heap)>=2:
           temp = self.heap[0]
             self.heap[0]=self.heap[len(self.heap)-1]
              self.heap[len(self.heap)-1]
             self.heap.pop( )
             self.down_adj( )
      elif len(self.heap)==1:
            self.heap.pop( )
  else:
            print("Nothing to pop")
 def swap(self,indexl, index2):
            temp = self.heap[index1]
            self.heap[indexl] = self.heap[index2]
            self.heap[index2] = temp

Execution

H = MaxHeap( )
print("*****pushing values***********")
print("pushing 165")
H.push(165)
print(H.heap)
print("pushing 60")
H.push (60)
print(H.heap)
print("pushing 179")
H.push(179)
print(H.heap)
print("pushing 400")
H.push(400)
print(H.heap)
print("pushing 6")
H.push(6)
print(H.heap)
print("pushing 275")
H.push(275)
print(H.heap)

Output

*****pushing values***********
pushing 165
[165]
pushing 60
[165, 60]
pushing 179
[179, 60, 165]
pushing 400
[400, 179, 165, 60]
pushing 6
[400, 179, 165, 60, 6]
pushing 275
[400, 179, 275, 60, 6, 165]
>>>

Question 6.
Write code to find out the maximum value in the Max heap.
Answer:
It is easy to find the max value in the max heap as the maximum value is available at the root node which is index 0 of the heap.

def peek(self):
print(self.heap[0])

Question 7.
Write the code to pop the maximum value from Max Heap.
Answer:
There are two steps involved:

  1. Swap the root with the last element in the array and pop the value.
  2. The root node now does have the maximum value. So, now we need to move downwards, comparing the parent with their left and right child to ensure that the children are smaller than the parent. If not we will have to swap places again.

Step 1

Define pop( ) function.

The pop( ) function which swaps the values of the root node with the last element in the list and pops the value. The function then calls the down_ adj( ) function which moves downwards and adjusts the values. The pop() function first checks the size of the heap. If the length of the heap is 1 then that means that it only contains one element that’s the root and there is no need to swap further.

def pop(self):
          if len(self.heap)>2:
              temp = self.heap[0]
              self.heap[0]=self.heap[len(self.heap)—1]
              self.heap[len(self.heap)-1]
              self.heap.pop ( )
              print("heap after popping largest value =", self.heap)
             self.down_adj( )
        elif len(self.heap)==1:
            self.heap.pop( )
     else:
          print("Nothing to pop")

Step 2:

Define downadj( ) function

Set Index Value To 0.
Index of left child = left_child = index*2+1 Index of right child = right child = index*2+2
Then we go through loop where we check the value of the parent with both left and right child. If the parent is smaller than the left child then we swap the value. We then compare the parent value with the right child, if it is less then we swap again.
This can be done as follows:

  • Check if parent is less than left child:
  • If yes, check if left child is less than the right child
  • if yes, then swap the parent with the right child
  • Change the value of index to value of right_child for further assessment
  • If no the just swap the parent with the left child
  • Set the value of index to value of left_child for further assessment
  • If the parent is not less than left child but only less than the right child then swap values with the right child
  • Change the value of index to right_child
def down_adj(self):
index = 0
for i in range(len(self.heap)//2):
left_child = index*2+1 .
if left_child > len (self.heap) :
return
print("left child = ", left_child)
right_child = index*2+2
if right_child > len (self.heap) :
return
print("right child = ", right_child)
if self.heap[index]<self.heap[left_child]:
temp = self.heap[index]
self.heap[index] = self.heap[left_child]
self.heap[left_child] = temp
index = left_child
if self.heap[index]<self.heap[right_child]:
temp = self.heap[index]
self.heap[index] = self.heap[right_child]
self.heap[right_child] = temp
index = right_child

Code

class MaxHeap:
      def_init_(self):
         self.heap = [ ]
    def push(self,value):
         self.heap.append(value)
         self .float_up (len (self, heap) -1)
def float_up (self, index) :
     if index==0:
           return
    else:
         if index%2==0:
               parent_of_index = (index//2)-1
               if self.heap[index]> self, heap[parent_of_index]:
                            temp = self.heap[parent_of_index]
                           self.heap[parent_of_index] =self.heap[index]
                              self.heap[index] = temp
         else:
               parent_of_index = index//2
               if self.heap[index]> self, heap[parent_of_index]:
                           temp = self.heap[parent_of_index]
                           self.heap[parent_of_index] = self.heap[index]
                           self.heap[index] = temp self.float_up (parent_of_index)
     def peek(self):
              print(self.heap [0])
     def pop(self):
           if len(self.heap)>=2:
                    temp = self.heap[0]
                    self.heap[0]=self.heap[len(self.heap)-1]
                    self.heap[len(self.heap)-1]
                    self.heap.pop( )
                    self.down_adj( )
                elif len(self.heap)==1:
                    self.heap.pop ( )
        else:
                 print ("Nothing to pop")
def swap(self,indexl, index2):
                temp = self.heap[index1]
                self.heap[indexl] = self.heap[index2]
                self.heap[index2] = temp

def down_adj(self):
       index = 0
       for i in range(len(self.heap)//2):
            left_child = index*2+1
            if left_child > len(self.heap)-1:
                 print(self.heap)
                 print("End Point")
                 print("Heap value after pop( ) =",self.heap)
     return
            right_child = index*2+2
            if right_child > len(self.heap)-1:
                   print ("right child does not exist")
                     if self.heap[index]<self.heap[left_child]:
                 self.swap(index,left_child)
                     index = left_child
                print("Heap value after pop( ) =", self.heap)
   return
                 if self.heap[index]<self.heap[left_child]:
                if self.heap[left_child]<self. heap[right_child]:
               self.swap(index,right_child) index = right_child 
      else:
                 self.swap(index,left_child) index = left_child
                  elif self.heap[index]<self.heap[right_child]:
           self.swap(index,right_child) index = right_child
   else:
           print("No change required" )
          print("Heap value after pop() = ", self.heap)

 

Execution

H = MaxHeap( )
print("*****pushing values***********")
H.push (165)
print(H.heap)
H.push(60)
print(H.heap)
H.push(179)
print(H.heap)
H.push(400)
print(H.heap)
H.push(6)
print(H.heap)
H.push(275)
print(H.heap)
print("*********popping values*******")
H.pop ( )
H.pop( )
H.pop ( )
H.pop ( )
H.pop ( )
H.pop ( )
H.pop ( )

Output

pushing values
[165]
[165, 60]
[179, 60, 165]
[400, 179, 165, 60]
[400, 179, 165, 60, 6]
[400, 179, 275, 60, 6, 165]
*********popping values*******
[275, 179, 165, 60, 6]
End Point
Heap value after pop( ) = [275, 179, 165, 60, 6]
right child does not'exist
Heap value after pop() = [179, 60, 165, 6]
Heap value after pop() = [165, 60, 6]
right child does not exist
Heap value after pop() = [60, 6]
Heap value after pop () = [6]
Nothing to pop
>>>

Question 8.
Time complexity for max heap:
Answer:

  • Insert: 0(log n)
  • Get Max: 0(1) because the maximum value is always the root at ‘ index( )
  • Remove Max: 0(log n)

The implementation of Min Heap is similar to Max Heap just that in this case the root has the lowest value and the value of parent is less than the left and right child.

Code

class MinHeap:
def_init_(self):
     self.heap = [ ]
def push(self,value):
      self.heap.append(value)
      self .float_up (len (self .heap) -1)
def float_up (self, index) :
   if index==0:
        return
else:
   if index%2==0:
        parent_of_index = (index//2)-1
        if self.heap[index]< self, heap[parent_of_index]:
        self.swap(index, parent_of_index)
else:
       parent_of_index = index//2
      if self.heap[index]< self, heap[parent_of_index]:
     self.swap(index, parent_of_index)
     self .floatyup (parent_of_index)
def peek(self) :
      print(self.heap [0])
def pop (self) :
     if len(self.heap)>=2:
     temp = self.heap[0]
     self.heap[0]=seIf.heap[len(self.heap)-1]
     self.heap[len(self.heap)-1]
     self.heap.pop ( )
     self.down_adj( )
  elif len(self.heap)==1:
     self.heap.pop()
else:
     print("Nothing to pop")
def swap(self,indexl, index2):
     temp = self.heap[index1]
     self.heap[indexl] = self.heap[index2]
     self.heap[index2] = temp

Execution

H = MinHeap( )
print("*****pushing values***********")
print("pushing 165")
H.push (165)
print(H.heap)
print("pushing 60")
H.push(60)
print(H.heap)
print("pushing 179")
H.push(179)
print(H.heap)
print("pushing 400")
H.push(400)
print(H.heap)
print("pushing 6")
H.push (6)
print(H.heap)
print("pushing 275")
H.push(275) '
print(H.heap)

Output

*****pushing values***********
pushing 165 [165]
pushing 60
[60, 165]
pushing 179
[60, 165, 179]
pushing 400
[60, 165, 179, 400]
pushing 6
[6, 60, 179, 400, 165]
pushing 275
[6, 60, 179, 400, 165, 275]
>>>

Question 9.
What are the applications of binary heap?
Answer:

  • Dijkstra Algorithm
  • Prims Algorithm
  • Priority Queue
  • Can be used to solve problems such as:
  • K’th Largest Element in an array
  • Sort an almost sorted array
  • Merge K Sorted Array

Question 10.
What is a priority queue and how can it be implemented?
Answer:
Priority queue is like a queue but it is more advanced. It has methods same as a queue. However, the main difference is that it keeps the value of higher priority at front and the value of lowest priority at the back. Items are added from the rear and removed from the front. In priority queue elements are added in order. So, basically there is a priority associated with every element. Element of highest priority is removed first. Elements of equal priority are treated ass per their order in queue.

Binary heap is the most preferred way of implementing priority queue as they can retrieve the element of highest priority in 0(1) time. Insert and delete can take O(logn) time. Besides that since, the binary heap use lists or arrays, it is easy to locate elements and the processes involved are definitely quite cache friendly. Binary heaps do not demand extra space for pointers and are much easier to insert.

Python Interview Questions on Python Internet Communication

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on Python Internet Communication

Question 1:
What are some supported socket communications protocol families?
Answer:
AF_INET (IPV4, TCP, UDP), AF_INET6 (IPV6, TCP, UDP), AF_UNIX

Question 2:
What are some socket types in Python?
Answer:
SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET

Question 3:
Illustrate opening a server side socket for incoming packets.
Answer:
import socket
mySock = socket.socket.(AFJNET, SOCK_STREAM)
mySock.bind((interfaceName, portNumber))
mySock.listen(3)

Question 4:
Illustrate opening a client-side socket for sending data.
Answer:
import socket
mySock = socket.socket.(AFJNET, SOCK_STREAM)
my Sock ,connect((interfaceName, portNumber))
mySock.send(data)

Question 5:
What module is used to send email?
Answer:
smtplib.

Question 6:
Illustrate connecting to a mail server, in preparation for sending an email.
Answer:
import smtplib
mySMTP = smtplib.SMTP(‘mail.example.org’)

Question 7:
Illustrate sending an email, then closing the connection.
Answer:
import smtplib
mySMTP = smtplib.SMTP(‘mail.example.org’)
myResult = mySMTP.sendmail(‘me@example.org’,
‘you@example.org’, message)
mySMTP.quit( )

Question 8:
What module is used to retrieve email from a server?
Answer:
poplib.

Question 9:
Illustrate retrieving the number of messages available on a mail server.
Answer:
Import poplib .
myPOP = poplib.POP3(‘mail.example.org’)
mPOP.user(“Me@example.org’)
mPOP.pass_(“myPassword”)
myMessageCount = len(mPOP.list()[l])

Question 10:
What module is used to support file transfers?
Answer:
ftplib.

Question 11:
Illustrate opening a FTP connection.
Answer:
import ftplib
myFTP = ftplib.FTP(/ftp.example.org,/ ‘anonymous’, ‘myemail@example.org’)

Question 12:
What method is used to retrieve a list of files on an active FTP connection?
Answer:
•dir( )

Question 13:
Illustrate using FTP to retrieve a file and write it to the local disk.
Answer:
import ftplib
myFTP = ftplib.FTP(‘ftp.example.org’, ‘anonymous’,
‘myemail@example.org’)
myFile = openC’localfile.txt”, “wb”)
myFTP.retbinaryCRETR remotefile.txt’, myFile.write)
myFTP.quit( ) .
myFile.close( )

Question 14:
What happens if the .quit() method is not called on FTP connections.
Answer:
A resource leak can occur if multiple FTP connections are opened but never closed.

Question 15:
Illustrate retrieving a list of files from an FTP server
Answer:
import ftplib
myFTP = ftplib.FTP(ftp.example.org’, ‘anonymous’,
‘myemail@example. org’)
myFileList = myFTP.dirO
print myFileList
myFTP.quit()

Question 16:
What does the SOCK_STREAM socket type signify?
Answer:
This is a TCP Socket type.

Question 17:
What does the SOCK_DGRAM signify?
Answer:
This is a UDP socket type.

Question 18:
How is the .shutdown method used?
Answer:
.shutdownO is called before .close to flush buffers and ensure a timely shutdown of the connection. It can be called using SHUTRD, SHUT_WR or SHUT_RDWR to end receiving, sending, or both.

Question 19:
What does socket.accept() return?
Answer:
The conn and address objects. Conn is a new socket object able to send or receive data, and the address object is the address of the remote host.

Question 20:
How is the remote address of a socket returned?
Answer:
Using the .getpeernameQ method of the connected socket.

Python Interview Questions on Searching and Sorting

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on Searching and Sorting

Sequential Search

In the chapter based on Python operators, we have seen that we can make use of the membership operator ‘in’ to check if a value exists in a list or not.

    >>> list.1 = [1,2,3,4,5,6,71
    >>> 3 in list1
   True
    >>> 8 in list1
    False
    >>>

This is nothing but searching for an element in a list. We are going to look at how elements can be searched and how process efficiency can be improved. We start by learning about sequential search.

The simplest way of searching for an element in a sequence is to check every element one by one. If the element is found then the search ends and the element is returned or else the search continues till the end of the sequence. This method of searching is known as linear search or sequential search. It follows a simple approach but it is quite an inefficient way of searching for an element because we move from one element to the other right from the beginning to end and if the element is not present in the sequence we would not know about it till we reach the last element.

Time analysis for sequential search:

  • Best scenario is that the element that we are looking for is the very first element in the list. In this case, the time complexity will be 0(1).
  • The worst scenario would be if we traverse throughout the entire sequence and realize that the element that we are looking for, does not exist. In this case, the time complexity will be O(n).

Python Interview Questions on Searching and Sorting chapter 14 img 1

Question 1.
Sequential search is also known as _______
Answer:
Linear Search

Question 2.
How are the elements reviewed in sequential search?
Answer:
The elements are reviewed one at a time in sequential terms.

Question 3.
When does the sequential search end?
Answer:
The sequential search ends when an element is found or when the end of the sequence is reached.

Question 4.
Write code to implement sequential search.
Answer:
The sequential search can be implemented as follows:

  • The function takes two values: seq list which is the list and target_ num which is the number to search in the list.
  • We set search_flag = 0 if the target number is found in the list we will set the search_flag to 1 else we let it be 0.
  • We iterate through the list comparing each element in the list with the target_num.
  • If a match is found we print a message and update the search_flag to 1.
  • After the “for” loop if the search_flag is still 0 then it means that the number was not found.

Code

def sequential__search (seq_list, target_num) :
    search_flag = 0
    for i in range(len(seq_list)):
        if seq_list[i] == target_num:
           print("Found the target number ", target_num, " at index", i,".")
            search_flag = 1;
    if search_flag == 0:
       print("Target Number Does Not Exist.Search Unsuccessful.")

Execution

seq_list = [1,2,3,4,5,6,7,8,2,9,10,11,12,13,14,15, 16]
target__num = input ("Please enter the target number : ")
sequential_search(seq_list, int(target_num))

Output 1

Please enter the target number: 5
Found the target number 5 at index 4.

Output 2

Please enter the target number: 2
Found the target number 2 at index 1.
Found the target number 2 at index 8.

Output 3

Please enter the target number: 87
Target Number Does Not Exist. Search Unsuccessful.

Question 5.
How would you implement a sequential search for an ordered list?
Answer:
When elements in a list are sorted, then many times there may not be the need to scan the entire list. The moment we reach an element that has a value greater than the target number, we know that we need not go any further.

Step 1: We define a function sequential_search() that takes two arguments – a list (seq_list) and the number that we are looking for (target num).

def sequential_search(seq_list, target_num):

Step 2: The first thing we do is set define a flag (search_flag) and set it to “False’’ or “0” value. The flag is set to “True” or “1” if the element is found. So, after traversing through the list if the search_flag value is still “False” or “0”, we would know that the number that we are looking for does not exist in the list.

def sequential__search (seq_list, target_num) :
    search_flag = 0

Step 3: Now, it’s time to check the elements one by one so, we define the for loop:

def sequential_search(seq_list, target_num):
    search_flag = 0
    for i in range(len(seq_list)):

Step 4: We now define how the elements are compared. Since it is an ordered list for every “i” in seq_list we have to check if i > target_num. If yes, then it means that there is no point moving further as it is an ordered list and we have reached an element that is greater than the number that we are looking for. However, if seq_list[i] == target_num then, the search is successful and we can set the search_flag to 1.

def sequential_search(seq_list, target^num):
    search_flag = 0
    for i in range(len(seq_list)):
        if seq_list[i] > target_num:
           print("search no further.")
           break;
       elif seq_list[i] == target_num:
            print("Found the target number ", target_num, " at index", i,".")
            search_flag = 1;

Step 5: After the for loop has been executed if the value of search_flag is still 0 then a message stating that the target number was not found must be displayed.

Code

def sequential_search(seq_list, target_num):
      search_flag = 0
      for i in range(len(seq_list)):
          if seq_list[i] > target_num:
             print("search no further.")
             break;
         elif seq_list[i] == target_num:
              print("Found the target number ", target_num, " at index", i,".")
              search_flag = 1 ;

      if search_flag == 0 ;
      print ("Target Number Does Not Exist. search Unsuccessful.")

Execution

seq_list = [1,2,3,4,5,6,7,8,2,9,10,11,12,13,14,15, 16]
target_num = input ("Please enter the target number : ")
sequential_search(seq_list, int(target_num))

Output 1

Please enter the target number: 2
Found the target number 2 at index 1.
Found the target number 2 at index 2.
search no further.
>>>

Output 2

Please enter the target number: 8
Found the target number 8 at index 8.
Search no further.
>>>

Output 3

Please enter the target number: 89
Target Number Does Not Exist. Search Unsuccessful.
>>>

Binary Search

A binary search is used to locate a target value from a sorted list. The search begins from the center of the sequence. The element present at the center is not equal to the target number it is compared with the target number. If the target number is greater than the element at the center then it means that we need to search for the number in the right half of the list and the left half need not be touched. Similarly, if the target number is less than the element present in the center then we will have to direct our search efforts towards the left. This process is repeated till the search is completed. The beauty of binary search is that in every search operation the sequence is cut into half and focus is shifted to only that half that has chances of having the value.

Python Interview Questions on Searching and Sorting chapter 14 img 2

Question 6.
Write a code to implement the binary search function.
Answer:
The binary search can be implemented in the following manner:
Step 1: Define the binary_search function. It takes 4 parameters:

  • sorted list: the input list that is in sorted form
  • target_num: the number that we are looking for
  • starting_point: the place from where we want to start searching, default value = 0
  • end_point: The endpoint for search, default value = None

Note that the list will be split in half in every step so the starting and ending point may change in every search operation.

def binary_search(sorted_list, target_num, start_ point=0, end_point=None):

Step 2: Do the following:

  • Set the search_flag to “False”
  • If the end_point is not provided, it would have the default value of “None”, set it to the length of the input list.
def binary_search(sorted_list, target_num, 
start_point=0, end_point=None):
     search_flag = False
     if end_point == None:
        end_point = len(sorted_list)-1

Step 3: Check, the start_point should be less than the endpoint. If that is true, do the following:

  • Get midpoint index value: mid_point = (end_point+start_point)//2
  • Check the value of the element at mid_point. Is it equal to the target_ num?
  • If sorted_lLst[midjioint] == target num1
  • Set search flag to True
  • If not check if the value at mid_point is greater than target_num :
  • sorted_list[mid_point] > target num
  • If yes, then we can discard the right side of the list now we can repeat the search from beginning to mid_point-1 value. Set
    endpoint to mid_point – 1. The starting point can remain the same(0).
  • The function should now call itself with:
  • sorted_list: same as before
  • target_num: same as before
  • starting point: same as before
  • endpoint: mid_point – 1
  • If not check if the value at mid_point is lesser than target num :
  • sorted_list[mid_point] < target_num
  • If yes, then the left side of the list is not required. We can repeat the search from mid_point+1 to the end of the list. Set starting point to mid_point+1. The ending_point can remain the same.
  • The function should now call itself with:
  • sorted_list: same as before
  • target_num: same as before
  • starting point: mid_point+l
  • end_point: same as before
  • If at the end of this procedure the search_flag is still set to “False”, then it means that the value does not exist in the list.

Code

def binary_search(sorted_list, target_num, start_ point=0, end_point=None):
       search_flag = False
       if end_point == None:
          end_point = len(sorted_list)-1
       if start_point < end_point:
          mid_point = (end_point+start_point)//2
          if sorted_list[mid_point] == target_num:
             search_flag = True
             print(target_num," Exists in the list at ",sorted_list.index(target_num))
             elif sorted_list[mid_point] > target_num:
                  end_point = mid_point-l 
                  binary_search(sorted_list, target_ num,start_point, end_point)
            elif sorted_list[mid_point] < target_num:
            start_point = mid_point+l 
            binary_search(sorted_list, target_num, start_point, end_point)
       elif not search_flag:
             print(target_num," Value does not exist")

Execution

sorted_list=[ 1,2,3,4,5,6,7,8,9,10,11,12,13]
binary_search(sorted_list, 14)
binary_search(sorted_list,0)
binary_search(sorted_list,5)

Output

14 Value does not exist 
0 Value does not exist 
5 Exists in the list at 4

Hash Tables

Hash Tables are data structures where a hash function is used to generate the index or address value for a data element. It is used to implement an associative array that can map keys to values. The benefit of this is that it allows us to access data faster as the index value behaves as a key for data value. Hash tables store data in key-value pairs but the data is generated using the hash function. In Python, the Hash Tables are nothing but Dictionary data type. Keys in the dictionary are generated using a hash function and the order of data elements in Dictionary is not fixed. We have already learned about various functions that can be used to access a dictionary object but what we actually aim at learning here is how hash tables are actually implemented.

We know that by binary search trees we can achieve the time complexity of O(logn) for various operations. The question that arises here is that can search operations be made faster? Is it possible to reach a time complexity of 0(1)11 This is precisely why hash tables came into existence? Like in a list or an array if the index is known, the time complexity for search operation can become 0(1). Similarly, if data is stored in key-value pairs, the result can be retrieved faster. So, we have keys and we have slots available where the values can be placed. If we are able to establish a relationship between the slots and the key it would be easier to retrieve the value at a fast rate. Look at the following figure:

Python Interview Questions on Searching and Sorting chapter 14 img 3

The key value is not always a nonnegative integer, it can be a string also, whereas the array has an index starting from 0 to length_of_array -1. So there is a need to do prewashing in order to match the string keys to indexes. So, for every key, there is a need to find an index in an array where the corresponding value can be placed. In order to do this, we will have to create a hash( ) function that can map a key of any type to a random array index.

During this process, there are chances of collision. Collision is when we map two keys to the same index as shown in the following figure:

Python Interview Questions on Searching and Sorting chapter 14 img 4

To resolve collision we can use chaining. Chaining is when values are stored -in the same slot with the help of a linked list as shown in the following figure:

Python Interview Questions on Searching and Sorting chapter 14 img 5

However, there can be cases of more than one collision for the same spot, and considering the worst-case scenario where there is a need to insert all values as elements of a linked list, it can be a tough situation that will have a severe impact on the time complexity. Worst case scenario will be if we land up placing all values as linked list elements at the same index.

To avoid this scenario we can consider the process of open addressing. Open addressing is the process of creating a new address. Consider a case where if there is a collision we increment the index by 1 and place the value there as shown below, there is collision while placing val3, as val2 already exists at index 1. So, the index value is incremented by 1 (1+1 =2) and val3 is placed at the index

Python Interview Questions on Searching and Sorting chapter 14 img 6

Had there been any other value at index 2 then the index would have incremented again and val3 could be placed at index 3. This means that this process of incrementing the index is continued till an empty slot is spotted. This is called Linear probing. Quadratic probing on the other hand increments by two times the index value. So, the search for the empty slots is done at a distance of 1,2,4,8, and so on. Rehashing is the process of hashing the result obtained again to find an empty slot.

The purpose of the hash function is to calculate an index from which the right value can be found therefore its job would be:

  1. To distribute the keys uniformly in the array.
  2. If n is the number of keys and m is the size of an array, the hash( ) = n%m(modulo operator) in case we use integers as keys.
  3. Prefer to use prime numbers both for array and the hash function for uniform distribution
  4. For string keys, you can calculate the ASCII value of each character and add them up and make a modulo operator on them

In many scenarios, hash tables prove to be more efficient than the search trees and are often used in caches, databases, and sets.
Important points:

  1. You can avoid clustering by using prime numbers.
  2. The number of entries divided by the size of the array is called the load factor.
  3. If the load factor increases the number of collisions will increase. This will reduce the performance of the hash table.
  4. Resize the table when the load factor exceeds the given threshold. However, this would be an expensive option as the hashes of the values entered will change whenever resizing is done and this can take O(n) to complete. Hence dynamic size array may be inappropriate for real-time scenarios.

Question 7.
What does the hash function do?
Answer:
The purpose of a hash function is to map the values or entries into the slots that are available in a hash table. So, for every entry, the hash function will compute an integer value that would be in the range of 0 to m-1 where m is the length of the array.

Question 8.
What is a remainder hash function? What are the drawbacks? Write the code to implement the remainder hash function.
Answer:
The remainder hash function calculates the index value by taking one item at a time from the collection. It is then divided by the size of the array and the remainder is returned.
h(item) = item% m, where m = size of the array Let’s consider the following array:
[18, 12,45,34, 89, 4]
The above array is of size 8.

Python Interview Questions on Searching and Sorting chapter 14 img 7

Drawback: You can see here that 18 and 34 have the same hash value of 2 and 12 and 4 have the same hash value of 4. This is a case of collision as a result when you execute the program, values 18 and 12 are replaced by 34 and 4 and you will not find these values in the hash table.

Let’s have a look at the implementation:

Step1: Define the hash function that takes a list and size of the array as input.
def hash(list_items, size):

def hash(list items, size) :

Step2: Do the following:

  • Create an empty list.
  • Now populate this key ‘from the numbers 0 to size mention. This
    example takes a list of 8 elements so we are creating a list [0, 1, 2, 3, 4, 5,6, 7].
  • Convert this list to diet using from keys( ). We should get a dictionary object of form {0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None}. This value is assigned to hash_table.
def hash(list_items, size):
temp_list =[ ]
for i in range(size):
      temp_list.append(i)
'hash_table = diet.fromkeys(temp_list)

Step3:

  • Now iterate through the list.
  • Calculate the index for every item by calculating item%size.
  • For the key value in the hash_table = index, insert the item.

Code

def hash(list_items, size):
     temp_list =[ ]
     for i in range(size):
          temp_list.append(i)
     hash_table = diet.fromkeys(temp_list)
     for item in list_items:
         i = item%size
         hash_table[i] = item
    print("value of hash table is : ",hash_table)

Execution

list_items = [18,12,45,34,89,4]
hash(list_items, 8)

Output

value of hash table is : {0: None, 1: 89, 2: 34,
3: None, 4: 4, 5: 45, 6: None, 7: None}
>>>

Question 9.
What is a folding hash function?
Answer:
The folding hash function is a technique used to avoid collisions while hashing. The items are divided into equal-size pieces, they are added together and then the slot value is calculated using the same hash function (item%size).

Suppose, we have a phone list as shown below:

phone_list= [4567774321, 4567775514, 9851742433, 4368884732]

We convert every number to a string, then each string is converted to a list, and then each list is appended to another list and we get the following result:
[[‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7\ ‘4’, ‘3’, ‘2’, ‘1’], [‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7’, ‘5’, ‘5’’, ‘1’, ‘4’], [‘9’, ‘8’, ‘5’, ‘1’, ‘7’, ‘4’, ‘2’, ‘4’, ‘3’, ‘3’], [‘4’, ‘3’, ‘6’, ‘8’, ‘8’, ‘8’, ‘4’, ‘7’, ‘3’, ‘2’]]

Now from this new list, we take one list item one by one, for every item we concatenate two characters convert them to integer, and then concatenate next to characters convert them to integer, and add the two values and continue this till we have added all elements. The calculation will be something like this:
[‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7’, ‘4’, ‘3’, ‘2’, ‘1’]

1. items =45
string val = 45
integer value = 45
hash value= 45

2. items =67
string val = 67
integer value = 67
hash value= 45+67 =112

3. items =77
string val = 77
integer value = 77
hash value= 112+77 =189

4. items =43
string val = 43
integer value = 43
hash value= 189+43 = 232

5. items =21
string val = 21 ,
integer value = 21
hash value= 232+21 = 253 Similarly,
[‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7’, ‘5’, ‘5’, ‘1’, ‘4’] will have a hash value of 511.
[‘9’, ‘8’, ‘5’, ‘1’, ‘7’, ‘4’, ‘2’, ‘4’, ‘3’, ‘3’] will have a hash value of 791.
[‘4’, ‘3’, ‘6\ ‘8’, ‘8’, ‘8’, ‘4’, ‘7’, ‘3’, ‘2’] will have a hash value of 1069.
We now call the hash function for [253, 531, 791, 1069] for size 11.
Python Interview Questions on Searching and Sorting chapter 14 img 8

So, the result we get is:
{0: 253, 1: None, 2: 1069, 3: None, 4: None, 5: 511, 6: None, 7: None, 8: None, 9: None, 10: 791}

Question 10.
Write the code to implement the coding hash function.
Answer:
Let’s look at the execution statements for this program:

phone_list = [4567774321, 4567775514, 9851742433, 4368884732]
str_phone_values = convert_to_string(phone_list)
folded_value = foldingjiash(str_phone_values)
folding_hash_table = hash(folded_value,11)
print(folding_hash_table)

1. A list of phone numbers is defined: phonejist = [4567774321, 4567775514, 9851742433, 4368884732]
2. The next statement “str_phonepy allies = convert_to_string(phone_ list)” calls a function convert to_string( ) and passes the phone_list as argument. The function in turn returns a list of lists. The function takes one phone number at a time converts it to a list and adds to new list. So, we get the output as: [[‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7’, ‘4’, ‘3’, ‘2’, M’], [‘4’, ‘5’, ‘6’, ‘7’, ‘7’, ‘7’, ‘5’, ‘5’, ‘1’, ‘4’], [‘9’, ‘8’, ‘5’, ‘1’, ‘7’, ‘4’, ‘2’, ‘4’, ‘3’, ‘3’], [‘4’, ‘3’, ‘6’, ‘8’, ‘8’, ‘8’, ‘4’, ‘7’, ‘3’, ‘2’]]. The following steps are involved in this function:

a. Define two lists a phone_list[ ]
b. For elements in phone_list, take every element i.e. the phone number one by one and:
i. convert the phone number to string: temp_string = str(i)
ii. Convert each string to list: tempjist = list (temp_string)
iii. Append the list obtained to the phone_list defined in previous step.
iv. Return the phone_list and assign values to str_phone_ values

def convert_to_string(input_list):
phone_list=[ ]
for i in input_list:
temp_string = str(i)
temp_list = list(temp_string)
phone_list.append(temp_list)
return phone_list

3. The list str_phone_values is passed on to folding_hash( ). This method takes a list as input.
a. It will take each phone list element which is also a list.
b. Take one list item one by one.
c. For every item concatenate first two characters convert them to integer and then concatenate next to characters convert them to integer and add the two values.
d. Pop the first two elements from thr list.
e. Repeat c and d till we have added all elements.
f. The function returns a list of hash values.

def folding_hash(input_list):
    hash_final = [ ]
    while len(input_list) > 0:
         hash_val = 0
         for element in input_list:
             while len(element) > 1:
                 stringl = element[0]
                 string2 = element[1]
                 str_contbine = string1 + string2
                 int_combine = int(str_combine)
                 hash_val += int_combine
                 element.pop(0)
                 element.pop(0)
             if len(element) > 0:
                hash_val += element[0]
             else:
                pass
             hash_final. append (hash_val)
        return hash final

4. Call hash function for size 11. The code for the hash function is same.

def hash(list_items, size):
    temp_list =[ ]
    for i in range (size) :
        temp_list.append(i)
    hash_table = diet.fromkeys(temp_list)
    for item in list_items:
        i = item%size
        hash_table[i] = item
    return hash_table

Code

def hash(list_items, size):
    temp_list =[ ]
    for i in range(size):
        temp_list.append(i)
    hash_table = diet.fromkeys(temp_list)
    for item in list_items:
        i = item%size
        hash_table[i] = item
   return hash_table
def convert_to_string(input_list):
    phone_list=[ ]
    for i in input_list:
        temp_string = str(i)
        temp_list = list(temp_string)
        phone_list.append(temp_list)
   return phone_list
def folding_hash(input_list):
    hash_final = [ ]
    while len(input_list) > 0:
         hash_val = 0
         for element in input_list:
             while len(element) > 1:
                 string1 = element[0]
                 string2 = element[1]
                 str_combine = string1 + string2
                 int_combine = int(str_combine)
                 hash_val += int_combine
                 element.pop(0)
                 element.pop(0)
            if len(element) > 0:
                hash_val += element[0]
           else:
              pass
           hash_final. append (hash_val)
    return hash_final

Execution

phone_list = [4567774321, 4567775514, 9851742433, 4368884732]
str_phone_values = convert_to_string(phone_list)
folded_value = folding_hash (str_phone_valu.es)
folding_hash_table = hash(folded_value,11)
print(folding_hash_table)

Output

{0: 253, 1: None, 2: 1069, 3: None, 4: None, 5:
511, 6: None, 7: None, 8: None, 9: None, 10: 791}

In order to store phone numbers at the index, we slightly change the hash() function;

  1. The hash( ) function will take one more parameter : phone_list
  2. After calculating the index the corresponding element from the phone_list is saved instead of the folded_value.
def hash(list_items,phone_list, size):
    temp_list =[ ]
    for i in range(size):
        temp_list.append(i)
   hash_table = diet.fromkeys(temp_list)
   for i in range(len(list_items)):
       hash_index = list_items[i]%size
       hash_table[hash_index] = phone_list[i]
   return hash_table

Execution

phone_list = [4567774321, 4567775514, 9851742433, 4368884732]
str_phone_values = ‘convert_to_string(phone_list)
folded_value = folding_hash(str_phone_values)
folding_hash_table = hash (folded_value,phone_ list,11)
print(folding_hash_table)

Output

{0: 4567774321, 1: None, 2: 4368884732, 3: None,
4: None, 5: 4567775514, 6: None, 7: None, 8: None,
9: None, 10: 9851742433}

Bubble sort

Bubble sort is also known as sinking sort or comparison sort. In bubble sort, each element is compared with the adjacent element, and the elements are swapped if they are found in the wrong order. However, this is a time-consuming algorithm. It is simple but quite inefficient.

Python Interview Questions on Searching and Sorting chapter 14 img 9

Question 10.
How will you implement bubble sort in Python?
Answer:
The code for a bubble sort algorithm is very simple.
Step 1: Define the function for bubble sort. It would take the list that needs
to be sorted as input.

def bubble_sort(input_list):

Step 2:
1. Set a loop for i in range len(input_list)
a. Inside this for loop set another loop for j in range len(input_ list)-i-1).
b. For every i, in the nested loop value at index j, is compared with the value at index j+1. If the value at index j+1 is smaller than the value at index j then the values are swapped.
c. After the for loop is overprint the sorted list.

Code

def bubble_sort(input_list):
    for i in range(len(input_list)):
        for j in range(len(input_list)-i-1):
            if input_list[j]>input_list[j+1]:
               temp = input_list[j]
               input_list[j]=input_list[j+1]
               input_list[j+1]= temp
    print(input_list)

Execution

x = [7,1,3,6,2,4]
print("Executing Bubble sort for ",x)
bubble_sort(x)

y = [23,67,12,3,45,87,98,34]
print("Executing Bubble sort for ",y)
bubble_sort(y)

Output

Executing Bubble sort for [7, 1, 3, 6, 2, 4]
[1, 2, 3, 4, 6, 7] 
Executing 98, 34] Bubble sort for [23, 67, 12, 3, 45, 87,
[3, 12, 23 , 34, 45, 67, 87, 98]

Question 11.
Write code to implement selection sort.
Answer:
Step 1: Define the function for selection_sort. It would take the list that
needs to be sorted as input.

def selection_sort(input_list):

Step 2:
1. Set a loop for i in range len(input_list)
a. Inside this for loop set another loop for j in range (i+1, len(input_ list)-i-l))
b. For every i, in the nested loop value at index j is compared with the value at index i. If the value at index j is smaller than the value at index i then the values are swapped.
c. After the for loop is overprint the sorted list.

Code

def selection_sort(input_list):
    for i in range(len(input list)-1) :
        for j in range(i+1,len(input list)):
            if input_list[j] < input_list[i]:
               temp = input_list[j]
               input_list[j] = input_list[i]
               input list[i] = temp
  print(input list)

Execution

selection_sort([15,10,3,19,80,85])

Output

[3, 10, 15, 19, 80, 85]

Insertion Sort

In insertion sort, each element at position x is compared with elements located at position x-1 to position 0. If the element is found to be less than any of the values that it is compared to then the values are swapped. This process is repeated till the last element has been compared.

Python Interview Questions on Searching and Sorting chapter 14 img 10

Question 12.
Write code to implement insertion sort.
Answer:
It is very easy to implement insertion sort:
Consider a list [7,1,3,6,2,4]
Let indexi = i
indexj = indexi + 1

Python Interview Questions on Searching and Sorting chapter 14 img 11

Step 1: Define the insert_sort( ) function. It will take input ist as input.

def insertion_sort(input_list):

Step 2: for i in range(input_list)-1), set indexi =1, indexj = i+1

for i in range(len(input_list)-1):
indexi = i
indexj = i+1

Step 3: set while loop, condition inaexi>=0

  • if input_list[indexi]>input_list[indexj]
  • swap values of input list [indexi] and input_list[indexj]
  • set indexi = indexi -1
  • set indexj = indexj -1
  • else
  • set indexi = indexi -1
while indexi >= 0:
            if input list[indexi]>input
list[indexj]:
               print("swapping")
               temp = input list indexi]
               input list[indexi] = input
list[indexj]
               input list[indexj] = temp
               indexi = indexi - 1
               indexj = indexj - 1
         else:
               indexi = indexi - 1

Step 4: Print updated list

Code

def insertion_sort(input_list):
    for i in range(len(input_list)-1):
        indexi = i
        indexj = i+1
        print("indexi = ", indexi)
        print("indexj = ", indexj)
        while indexi >= 0:
              if input_list[indexi]>input_ list[indexj]:
                            print("swapping")
                            temp = input_list[indexi]
                            input_list[indexi] = input
list[indexj]
                            input_list[indexj] = temp
                            indexi = indexi - 1
                            indexj = indexj - 1
                      else :
                            indexi = indexi - 1
                 print("list update:",input_list)
        print ("final list = ", input_list)

Execution

insertion_sort([9,5,4,6,7,8,2])

Output

[7, 1, 3, 6, 2, 4 ]
indexi = 0
indexj = 1
swapping
list update: [1, 7, 3, 6, 2, 4 ]
indexi = 1
indexj = 2
swapping
list update: [1, 3, 7, 6, 2, 4 ]
indexi = 2
indexj = 3
swapping
list update: [1, 3, 6, 7, 2, 4 ]
indexi = 3
indexj = 4
swapping
swapping
swapping
list update: [ 1, 2, 3,6,7,4]
indexi = 4
indexj = 5
swapping
swapping
list update: [1, 2, 3, 4, 6, 7]
final list = [1, 2, 3, 4, 6, 7]
>>>

Shell Sort

  • Shell sort is a very efficient sorting algorithm.
  • Based on insertion sort.
  • Implements insertion sort on widely spread elements at first and then in each step space or interval is narrowed down.
  • Great for medium size data set.
  • Worst case time complexity: O(n)
  • Worst-case space complexity : 0(n)

I Consider a list: [10,30,11,4,36,31,15,1]
Size of the list, n = 8
Divide n/2 = 4, let this value be named k
Consider every kth(in this case 4th) element and sort them in the right order.

Python Interview Questions on Searching and Sorting chapter 14 img 12

II. do the following:
k = k/2 = 4/2 = 2
consider every kth element and sort the order.

Python Interview Questions on Searching and Sorting chapter 14 img 13

III. Do the following:
k = k/2 = 2/ 2 = 1
This is the last pass and will always be an insertion pass.

Python Interview Questions on Searching and Sorting chapter 14 img 14

Question 13.
Write code to implement the shell sort algorithm.
Answer:
The following steps will be involved:
Step1: Define the shell_sort( ) function to sort the list. It will take the list(input_list) that needs to be sorted as the input value.

def shell_sort(input_list):

Step2:
Calculate size, n = len(inputjist)
Number of steps for the while loop, k = n/2

def shell_sort(input_list):
n = len(input_list)
k = n//2

Step 3:

  • While k > 0:
  • for j in range 0, size of input list
  • for i in range (k, n)
  • if the value of element at i is less than the element located at index i-k, then swap the two values
  • set k = k//2
while k > 0:
     for j in range(n):
         for i in range(k,n):
             temp = input_list[i]
             if input_list[i] < input_list[i-k]:
                input_list[i] = input_list[i-k]
                input_list[i-k] = temp
    k = k//2

Step 4: Print the value of the sorted list.

Code

def shell_sort(input_list):
    n = len(input_list)
    k = n//2
    while k > 0:
         for j in range.(n) :
             for i in range(k,n):
                 temp = input_list[i]
                 if input_list[i] < input_list[i-k]:
                     input_list[i] = input_list[i-k]
                     input_list[i-k] = temp
        k = k//2
   print(input_list)

Execution

shell_sort ([10, 30, 11, 1,36, 31, 15, 4)]

Output

[1, 4, 10, 11, 15, 30, 31, 36]

Quick sort

  • In quicksort, we make use of a pivot to compare numbers.
  • All items smaller than the pivot are moved to its left side and all items larger than the pivot are moved to the right side.
  • This would provide a left partition that has all values less than the pivot and a right partition having all values greater than the pivot.
  • Let’s take a list of 9 numbers: [15, 39, 4, 20, 50, 6, 28, 2, 13].
  • The last element ‘ 13 ’ is taken as the pivot.
  • We take the first element ‘15’ as the left mark and the second last element ‘2’ as the right mark.
  • If left mark > pivot and Highmark <pivot then swap left a mark and right mark and increment left mark by 1 and decrement right make by 1.
  • If leftmark> pivot and rightmark> pivot then only decrement the right mark.
  • Same way if leftmark<pivot and rightmark< pivot then only increment the left mark.
  • If left mark <pivot and rightmark>pivot, increment leftmark by 1 and decrement right mark by 1.
  • When the left mark and right mark meet at one element, swap that element with the pivot.

I
The updated list is now [2, 6, 4, 13, 50, 39, 28, 15, 20]:

  • Take the elements to the left of 13, takin 4 as a pivot, and sort them in the same manner.
  • Once the left partition is sorted take the elements on the right and sort them taking 20 as a pivot.

Python Interview Questions on Searching and Sorting chapter 14 img 15

II.

Python Interview Questions on Searching and Sorting chapter 14 img 16

Question 14.
Write the code to implement the quicksort algorithm.
Answer:
Step: Decide upon the Pivot

  • This function takes three parameters, the list(input list), starting (fast) and ending (last) index for the list that has to be sorted.
  • Take the input_list. pivot = input_list[last]. We set the pivot to the last value of the list.
  • Set the left_pointer to first.
  • Set right_pointer to last -1, because the last element is the pivot.
  • Set pivot flag to True.
  • While pivot_flag is true:
  • If left mark > pivot and right mark <pivot then swap left mark and right mark and increment left mark by 1 and decrement right mark by 1.
  • If leftmark> pivot and rightmark> pivot then only decrement the rightmark.
  • Same way if leftmark<pivot and rightmark< pivot then only increment the leftmark.
  • If leftmark <pivot and rightmark>pivot, increment leftmark by 1 and decrement right mark by 1.
  • When left mark and rightmark meet at one element, swap that element with the pivot.
  • When, leftmark >= rightmark, swap the value of the pivot with the element at left pointer, set the pivot_flag to false.
def find_pivot (input_list, first, last):
    pivot = input_list[last]
    print("pivot =", pivot)
    left_pointer = first
    print("left pointer = ", left_pointer, " ",input_list[left_pointer])
    right_pointer = last-1
    print("right pointer = ", right_pointer, " ",input_list[right_pointer])
    pivot_flag = True

   while pivot_flag:
         if input_list[left_pointer]>pivot:
            if input_list[right_pointer]<pivot:
                temp = input_list[right_pointer]
                input_list[right_pointer]=input_ list[left_pointer]

                input_list[left_pointer]= temp
                right_pointer = right_pointer-1
                left_pointer = left_pointer+1

       else:
                right_pointer = right_pointer-1
  else:
        left_pointer = left_pointer+1
        right_pointer = right_pointer-1
  if left_pointer >= right_pointer:
        temp = input_list[last]
  input_list[last] = input_list[left_pointer]
  input_list[left_pointer] = temp
  pivot_flag = False
print(left_pointer)
return left_pointer

Step 2:
Define quicksort(input list) function.

  • This function will take a list as input.
  • Decides the starting point(O) and end point(length_of_the_list-1) for sorting.
  • Call the qsHelper( ) function.
def quicksort(input_list):
    first = 0
    last = len(input_list)-1
    qsHelper (input_list,first, last)

Step 3:
Define the qsHelper( ) function

This function checks the first index and last index value, it is a recursive function, it calls the find_pivot method where left mark is incremented by 1 and the right mark is decremented by 1. So, as long as the left mark(which is parameter first in this case) is less than the right mark(which is parameter last in this case) the while loop will be executed where qsHelper finds a new value of pivot, creates; eft and right partition and calls itself.

def qsHelper (input_list,first, last) :
    if first<last:
         partition = find_pivot (input_ list, first, last)
         qsHelper(input_list, first,partition-1)
         qsHelper(input_list,partition+l,last)

Code

def find_pivot (input_list, first, last):
    pivot = input_list[last]
    left_pointer = first
    right_pointer = last-1
    pivot_flag = True

    while pivot_flag:
          if input_list[left_pointer]>pivot:
             if input_list[right_pointer]<pivot:
                temp = input_list[right_pointer]
                input_list[right_pointer]=input_ list[left_pointer]
                input_list[left_pointer]= temp
                right_pointer = right_pointer-l left_pointer = left_pointer+l 
        else:
                right_pointer = right_pointer-l
        else:
               if input_list[right_pointer]<pivot:
                 left_pointer = left_pointer+l 
              else:
                 left_pointer = left_pointer+l 
                 right_pointer = right_pointer-1
            if left_pointer >= right_pointer: 
            temp = input_list[last]
            input_list[last] = input_list[left_pointer]

            input_list[left_pointer] = temp 
            pivot_flag = False 
            return left_pointer 
def quicksort(input_list): 
first = 0 
last = len(input_list)-1 
qsHelper (input_list, first, last)
def qsHelper (input_list,first, last) : 
if firstclast:
partition = find_pivot (input_list,first, last) 
qsHelper (input_list, first, partition-1) 
qsHelper(input_list,partition+1,last)

Execution

input_list=[15,39,4,20, 50, 6,28,2, 13]
quicksort(input_list)
print(input list)

Output

[2,4, 6, 13, 15, 20, 28, 39, 50]

Python Interview Questions on Linked List

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on Linked List

A linked list is a linear structure that consists of elements such that each element is an individual object and contains information regarding:

  1. Data
  2. Reference to next element

In the linked list, each element is called a node.

Python Interview Questions on Linked List chapter 11 img 3

You can see in the diagram, the reference to the first node is called Head. It is the entry point of the linked list. If the list is empty then the head points to null. The last node of the linked list refers to null.

As the number of nodes can be increased or decreased as per requirement, linked lists are considered to be dynamic data structures. However, in a linked list direct access to data is not possible. Search for any item that starts from- the head and you will have to go through each reference to get that item. A linked list occupies more memory.

The linked list described above is called a singly linked list. There is one more type of linked list known as a doubly-linked list. A double-linked list has a reference to the next node and the previous node.

Python Interview Questions on Linked List chapter 11 img 4

Question 1.
Write a code to implement a Node class such that a Node contains data as well as a reference to the next node.
Answer:
To create a node object we pass data value to the constructor. The constructor assigns the data value to data and sets the node’s reference to None. Once we create all the objects we assign the memory address of the second object as the reference to the first node object, the memory address of the third object is assigned as a reference to the second object, and so on. The last object, thus have no(or none as) reference.

The code for the Node class will be as follows:

Code

class Node:
        def__init__(self,data = None):
            self . data = data
            self . reference = None
objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
objNode1 . reference = objNode2
objNode2 . reference = objNode3
objNode3 . reference = objNode4
objNode4 . reference = None

Execution

print("DATA VALUE = ",objNodel.data,"REFERENCE = ",objNodel.reference)
print("DATA VALUE = ",objNode2.data,"REFERENCE = ",objNode2.reference)
print("DATA VALUE = ",objNode3.data,"REFERENCE = ",objNode3.reference)
print("DATA VALUE = ",objNode4.data,"REFERENCE = ”,objNode4.reference)

Output

DATA VALUE = 1 REFERENCE = <_main_. Node object
at 0x000000595E0CC6A0>
DATA VALUE = 2 REFERENCE =<_main_ .Node object
at 0x000000595E329978>
DATA VALUE = 3 REFERENCE = <_main_ .Node object
at 0x000000595E3299B0>
DATA VALUE = 4 REFERENCE = None
>>>

Question 2.
Write code to traverse through a linked list.
Answer:
Method I
We have already written the code for the Node class:

class Node:
      def _init_ (self,data = None):
          self.data = data
          self.reference = None

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)

We will now see how to traverse through the linked list:

Step 1
We create a variable presentNode and assign the first object to it.

presentNode = objNode1

On doing this presentNode gets the data and reference values of objectNode1.
Step 2
The reference value points to objNode2.
So, we can write a while loop:

while presentNode:
print("DATA VALUE = ",presentNode.data)
presentNode = presentNode.reference

Once the presentNode is assigned, the reference value contained in objNode4 it will come out of the while loop because the value of reference is None.

Code

class Node:
       def_init_(self,data = None):
           self.data = data
           self.reference = None

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
objNodel.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
objNode4.reference = None
presentNode = objNode1
while presentNode:
    print("DATA VALUE = ",presentNode.data)
    presentNode = presentNode.reference

Output

DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 4

Method II
Another method to do this by creating two classes: Node and Linked list
Code

class Node:
      def_init_(self,data = None):
          self.data = data
          self.reference = None
class Linked_list:
     def_init_(self):
        self.head = None 
     def traverse(self) :
           presentNode = self.head
           while presentNode:
                  print("DATA VALUE = ",presentNode.data)
                  presentNode = presentNode.reference

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNodel
# reference of the first node object to second object

linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj.traverse ( )

Output

DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 4

Question 3.
Write a code to add a node at the beginning of a linked list.
Answer:
To solve this question, we will just add a new method to insert the node in the same code that is mentioned in the last example:
In the last example, we pointed the head of the linked list object to the first node object.

linkObj.head = objNode1

When we add the node at the beginning we just have to make the linkObj.
head = new_node and new node.reference = obj_Node1.

For this, we write a code where the value of the linkObj.head is first passed on to the new node. reference and then linkObj.head is set to the new node object.

def insert_at_Beginning(self, data) :
new_data = Node(data)
new_data.reference = self.head
self.head = new_data

So, the full code would be like the following:

Code

class Node:
     def_init_(self,data = None):
          self.data = data
          self.reference = None
class Linked_list:
   def_init_(self) :
         self.head = None
  def traverse(self):
       presentNode = self.head
       while presentNode:
            print("DATA VALUE = ",presentNode.data)
            presentNode = presentNode.reference
 def insert_at_Beginning(self, data) :
       new_data = Node(data)
       new_data.reference = self.head
       self.head = new data

Execution

objNode1 = Node(l1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNode1
# reference of the first node object to second object
linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj.insert_at_Beginning(5)
linkObj.traverse ( )

Output

DATA VALUE = 5
DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 4

Question 4.
Write a code to add a node at the end of a linked list.
Answer.
In order to add a node at the end of the linked list, it is important that you point the reference of the last node to the new node that you create.
Step 1:

Define the function

def insert_at_end(self,data):

Step 2:

Create a new Node object

new_data = Node(data)

Step 3:

Traverse through the linked list to reach the last node
Remember that you cannot directly access the last node in the linked list. You will have to traverse through all the nodes and reach the last node in order to take the next step.

presentNode = self.head
             while presentNode.reference != None:
                   presentNode = presentNode.reference

Step 4:

Add the new node at the end

After traversing through the linked list, you know that you have reached the last node whenpresentNode.reference = None. Since this won’t remain the last node anymore, you need to:

presentNode.reference = new_data

With this, we have added a new node at the end of a linked list.

Code

class Node:
    def_init_(self,data = None):
          self.data = data
          self.reference = None
class Linked_list:
   def_init_(self):
         self.head = None
   def traverse(self):
       presentNode = self.head
       while presentNode:
             print("DATA VALUE = ",presentNode.data)
             presentNode = presentNode.reference
  def insert_at_end(self,data):
      new_data = Node(data)
      presentNode = self.head
      while presentNode.reference != None:
          presentNode = presentNode.reference
      presentNode.reference = new_data

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNode1
# reference of the first node object to second object
linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj.insert_at_end(5)
linkObj.insert_at_end(6)
linkObj.insert_at_end(7)
linkObj.traverse( )

Output

DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 4
DATA VALUE = 5
DATA VALUE = 6
DATA VALUE = 7

Question 5.
Write a code to insert a node between two nodes in a linked list.
Answer:
The solution for this problem is very similar to adding a node to the beginning. The only difference is that when we add a node at the beginning we point the head value to the new node whereas in this case, the function will take two parameters. First will be the node object after which the new object will be inserted and the second would be the data for the new object. Once the new node is created, we pass on the reference value stored in the existing node object to it and the existing node is then made to point at the new node object

Step 1:

Define the function

This function will take two parameters:

  1. The node object after which the data is to be inserted.
  2. Data for the new node object.
def insert_in_middle(self,insert_data,new_data):

Step 2:
Assign references

new_node = Node(new_data)
new_node.reference = insert_data.reference insert_data.reference = new_node

Code

class Node:
      def_init_(self,data = None):
               self.data = data
               self.reference = None
class Linked_list:
def_init_(self):
self.head = None
def traverse(self):
presentNode = self.head
while presentNode:
                    print("DATA VALUE = ",presentNode.data)
                    presentNode = presentNode.reference


def insert_in_middle(self,insert_data,new_data):
                 new_node = Node(new_data)
                new_node.reference = insert_data.reference insert_data.reference = new_node

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list()
#head of the linked list to first object
linkObj.head = objNodel
# reference of the first node object to second object
linkObj.head.reference = objNcde2

objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj.insert_in_middle(objNode3,8)
linkObj.traverse ( )

Output

DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 8
DATA VALUE = 4
>>>

Question 6.
Write code to remove a node from a linked list.
Answer:
Suppose we have a linked list as shown below:
A -> B -> C
A. reference = B
B. reference = C
C. reference = A.
To remove B, we traverse through the linked list. When we reach node A that has a reference pointing to B, we replace that value with the reference value stored in B(that points to C). So that will make A point to C and B is removed from the chain.
The function code will be as follows:

def remove(self,removeObj):
 presentNode = self.head
  while presentNode:
    if(presentNode.reference == removeObj):
       presentNode.reference = removeObj.reference
           presentNode = presentNode.reference

The function takes the Node object as a parameter and traverses through the linked list, till it reaches the object that needs to be removed. Once we reach the node that has reference to the node that has to be removed, we simply change the reference value to reference value stored in object removeobj. Thus, the node now points directly to the node after the removeObj.

Code

class Node:
  def_init_(self,data = None):
           self.data = data
           self.reference = None
class Linked_list:
  def_init_(self) :
         self.head = None
  def traverse(self):
          presentNode = self.head
          while presentNode:
             print("DATA VALUE = ",presentNode.data)
             presentNode = presentNode.reference


def remove(self,removeObj):
      presentNode = self.head
       while presentNode:
             if(presentNode.reference == removeObj):
             presentNode.reference = removeObj.
reference
             presentNode = presentNode.reference

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNode1
# reference of the first node object to second object
linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj.remove(objNode2)
linkObj.traverse( )

Question 7.
Print the values of the node in the center of the linked list.
Answer:
This involves counting the number of nodes in the object. If the length is even then the data of two nodes in the middle should be printed else only the data of the node in the center should be printed.

Step 1:

Define the function

def find_middle (self, 1list) :

Step 2:

Find the length of the counter

Here, we set a variable counter = 0. As we traverse through the linked list we increment the counter. At the end of the while loop we get the count of the number of nodes in the linked list. This is also the length of the linked list.

counter = 0
          presentNode = self.head
          while presentNode:
                 presentNode = presentNode.reference
                  counter = counter + 1 .
          print("size of linked list = ", counter)

Step 3:

Reach the middle of the linked list

The reference to the node in the middle is stored in the node before that. So, in the for loop instead of iterating (counter/2) times, we iterate (counter-1)/2 times. This brings us to the node which is placed just before the centre value.

presentNode = self.head
for i in range((counter-1)112) :
presentNode = presentNode.reference

Step 4:

Display the result depending on whether the number of nodes in the linked list

If the linked list has an even number of nodes then print the value of reference stored in the present node and the next node.

if (counter%2 == 0):
                nextNode = presentNode.reference
                print ("Since the length of linked list is an even number the two midle elements are:")
                print(presentNode.data,nextNode.data)

Else, print the value of the present node.

else: 
                print("Since the length of the linked list is an odd number, the middle element is: ")
             print(presentNode.data)

Code

class Node:
     def_init_(self,data = None):
           self.data = data
           self.reference = None
class Linked_list:
    def_init_(self):
          self.head = None
def find_middle (self, Hist) :
     counter = 0
     presentNode = self.head
    while presentNode:
         presentNode = presentNode.reference
         counter = counter + 1
    print ("size of linked list = ", counter)
    presentNode = self.head
    for i in range((counter-1)112) :
        presentNode = presentNode.reference
   if (counter%2 == 0) : 
          nextNode = presentNode.reference
          print("Since the length of linked list is an even number the two midle elements are:")
         print(presentNode.data,nextNode.data)


     else:
            print("Since the length of the linked list is an odd number, the middle element is: ")
           print(presentNode.data)

Execution (Odd Number of Nodes)

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
objNode5 = Node(5)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNode1
# reference of the first node object to second object
linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
objNode4.reference = objNode5
linkObj .find_middle (linkObj )

Output

size of linked list = 5
Since the length of the linked list is an odd 
number, the middle element is:
3

Execution (Even Numbers)

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4) 
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNodel
# reference of the first node object to second object
linkObj.head.reference = objNode2
objNode2.reference = objNode3
objNode3.reference = objNode4
linkObj .find_middle (linkObj )

Output

size of linked list = 4
Since the length of the linked list is an even number the two middle elements are:
2  3

Question 8.
Implement a doubly linked list.
Answer:
A doubly linked list has three parts:

  1. Pointer to the previous node
  2. Data
  3. Pointer to the next node

Implementation of a doubly linked list is easy. We just need to take care of one thing that each node is connected to the next and the previous data.

Step 1:

Create a Node Class

The node class will have a constructor that initializes three parameters: data, reference to next node – refNext and reference to previous node – refPrev.

class Node:
          def_init_(self,data = None):
              self.data = data
              self.refNext = None
              self.refPrev = None

Step 2:

Create functions to traverse through the double linked list

I. Traverse Forward

To: traverse forward with the help of refNext that points to next value of the linked list. We start with the head and move on to the next node using
refNext.

def traverse(self):
presentNode = self.head
while presentNode:
print("DATA VALUE = ",presentNode.data)
presentNode = presentNode.refNext

II. Traverse Reverse

The traverse reverse is the opposite of the traverse forward. We are able to traverse backward with the help of the value of refPrev because it points to the previous node. We start from the tail and move on to the previous node using
refPrev.

def traverseReverse(self):
presentNode = self.tail
while presentNode:
        print("DATA VALUE = ",presentNode.data)
         presentNode = presentNode.refPrev

Step 3:

Write a function to add a node at the end

Appending a node at the end of the doubly linked list is the same as appending in the linked list the only difference is that we have to ensure that the node that is appended has its refPrev pointing to the node after which it has been added.

def append(self,data):
new_data = Node(data)
presentNode = self.head
while presentNode.refNext != None:
presentNode = presentNode.refNext
presentNode.refNext = new_data
new_data.refPrev = presentNode

Step 4:

Write a function to remove a node

This function takes the node object that needs to be removed as a parameter. In order to remove a node, we iterate through the doubly linked list twice. We first start with the head and move forward using refNext and when we encounter the object that needs to be removed we change the refNext value of the present node (which is presently pointing to the object that needs to be removed) to the node that comes after the object that needs to be removed. We then traverse through the linked list backward starting from the tail and when we encounter the object to be removed again we change the refPrev value of the present node to the node that is placed before it.

def remove(self,removeObj):
presentNode = self.head
presentNodeTail = self.tail
while presentNode.refNext != None:
     if(presentNode.refNext == removeObj):
          presentNode.refNext = removeObj.refNext
     presentNode = presentNode.refNext
 while presentNodeTail.refPrev != None:
    if(presentNodeTail.refPrev == removeObj):
        presentNodeTail.refPrev = removeObj.
refPrev
      presentNodeTail = presentNodeTail.refPrev

Code

class Node:
  def_init_(self,data = None):
      self.data = data
      self.refNext = None
      self.refPrev = None
class dLinked_list:
 def_init_(self)':
      self.head = None
      self.tail = None
def append(self, data) :
    new_data = Node(data)
    presentNode = self.head
    while presentNode.refNext != None:
         presentNode = presentNode.refNext
        presentNode.refNext = new_data
        new_data.refPrev = presentNode
       self.tail = new data


def traverse(self):
     presentNode = self.head
     while presentNode:
        print("DATA VALUE = ",presentNode.data)
        presentNode = presentNode.refNext


def traverseReverse(self):
    presentNode = self.tail
    while presentNode:
          print("DATA VALUE = ",presentNode.data)
          presentNode = presentNode.refPrev
def remove(self,removeObj):
    presentNode = self.head
    presentNodeTail = self.tail
    while presentNode.refNext != None:
       if (presentNode.refNext == removeObj):
           presentNode.refNext = removeObj.
refNext
          presentNode = presentNode.refNext
   while presentNodeTail.refPrev != None:
          if (presentNodeTail.refPrev ==
removeObj):
         presentNodeTail.refPrev = removeObj.
refPrev
        presentNodeTail = presentNodeTail.
refPrev

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
ObjNode4 = Node(4)
dlinkObj = dLinked_list( )
#head of the linked list to first object
dlinkObj.head = objNode1
dlinkObj.tail = objNode4
# reference of the first node object to second object
dlinkObj.head.refNext = objNode2
dlinkObj.tail.refPrev = objNode3
objNode2.refNext = objNode3
objNode3.refNext = objNode4
objNode4.refPrev = objNode3
objNode3.refPrev = objNode2
objNode2.refPrev = objNode1
print("Appending Values")
dlinkObj.append(8)
dlinkObj.append(9)
print("traversing forward after Append")
dlinkObj.traverse( )
print("traversing reverse after Append")
dlinkObj.traverseReverse( )
print("Removing Values")
dlinkObj.remove(objNode2)
print("traversing forward after Remove")
dlinkObj.traverse( )
print("traversing reverse after Remove")
dlinkObj.traverseReverse( )

Output

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
dlinkObj = dLinked_list( )
#head of the linked list to first object
dlinkObj.head = objNode1
dlinkObj.tail = objNode4
# reference of the first node object to second object
dlinkObj.head.refNext = objNode2
dlinkObj.tail.refPrev = objNode3
objNode2.refNext = objNode3
objNode3.refNext = objNode4
objNode4.refPrev = objNode3
objNode3.refPrev = objNode2
objNode2.refPrev = objNode1
print("Appending Values")
dlinkObj.append(8)
dlinkObj.append(9)
print("traversing forward after Append")
dlinkObj.traverse( )
print("traversing reverse after Append")
dlinkObj.traverseReverse( )
print("Removing Values")
dlinkObj.remove(objNode2)
print("traversing forward after Remove")
dlinkObj.traverse( )
print("traversing reverse after Remove")
dlinkObj.traverseReverse( )

Question 9.
Write code to reverse a linked list.
Answer:
To reverse a linked list we have to reverse the pointers. Look at the figure shown below. The first table shows how information is stored in the linked list. The second table shows how the parameters are initialized in the reverse( ) function before beginning to traverse through the list and reversing the elements.

Python Interview Questions on Linked List chapter 11 img 1

We then use the following while loop:

while nextval != None:
presentNode.refNext = previous
previous = presentNode
presentNode = nextval
nextval = nextval.refNext
presentNode.refNext = previous
self.head = presentNode

This is how the while loop works:

Python Interview Questions on Linked List chapter 11 img 2

You can see as we iterate through the while loop how the values of presentnode.refNext change. Node1 that was earlier pointing to node2 changes its pointer to none. Same way node2 changes its pointer value to node1, and so on.
Code

class Node:
def_init_(self,data = None):
      self.data = data
      self.refNext = None
class Linked_list:
     def init (self) :
     self.head = None


def reverse(self):
     previous = None
     presentNode = self.head
     nextval = presentNode.refNext
     while nextval != None:
           presentNode.refNext = previous
           previous = presentNode
           presentNode = nextval
           nextval = nextval.refNext
    presentNode.refNext = previous
    self.head = presentNode
def traverse(self) :
    presentNode = self.head
   while presentNode:
     print("DATA VALUE = ",presentNode.data)
     presentNode = presentNode.refNext

Execution

objNode1 = Node(1)
objNode2 = Node(2)
objNode3 = Node(3)
objNode4 = Node(4)
linkObj = Linked_list( )
#head of the linked list to first object
linkObj.head = objNode1
# reference of the first node object to second object
linkObj.head.refNext = objNode2
obj'Node2 . refNext = objNode3
objNode3.refNext = objNode4
print("traverse before reversing")
linkObj.traverse( )
linkObj.reverse( )
print("traverse after reversing")
linkObj.traverse( )

Output

traverse before reversing
DATA VALUE = 1
DATA VALUE = 2
DATA VALUE = 3
DATA VALUE = 4
traverse after reversing
DATA VALUE = 4
DATA VALUE = 3
DATA VALUE = 2
DATA VALUE = 1

 

 

Python Interview Questions on Python Database Interface

We have compiled most frequently asked Python Interview Questions which will help you with different expertise levels.

Python Interview Questions on Python Database Interface

Question 1:
What is the generic Python “built-in” database module called?
Answer:
DBM

Question 2:
What are some advantages of using DBM?
Answer:
No back-end database is required. The files are portable and work with the pickle and shelve modules to store Python objects.

Question 3:
What does it mean to pickle and unpickle a Python object?
Answer:
Using the pickle module, allows the writing of Python objects to a file. Unpickling is the reverse, reading a file containing a pickled Python object and re-instantiating it in the running program.

Question 4:
What is the difference between the pickle and cPickle Python modules?
Answer:
cPickle is much faster, but it cannot be subclassed like pickle can.

Question 5:
What is the shelve module, and how is it different from pickle?
Answer:
Shelving an object is a higher-level operation that pickles, in that the shelve module provides its own open method and pickles objects behind the scene. Shelve also allows the storage of more complex Python objects.

Question 6:
Illustrate creating a DBM file.
Answer:
import anydbm
myDB = anydbm.open(“newDBFile.dbm”, ‘n’)

Question 7:
Illustrate writing a list to a DBM file.
Answer:
import anydbm
my List = [“This”, “That”, “Something Else”]
myDB = anydbm.open(“newDBFile.dbm”, ‘w’)
mylndex = 0
for myltem in myList:
myDB[my!tem] = myList[myIndex]
mylndex += 1 myDB.close( )

Question 8:
Illustrate pickling a list.
Answer:
import cPickle .
myList = [“This”, “That”, “Something Else”]
myFile = open(“myPickleFile.dat”, “w”)
myPickler = cPickle.Pickler(myFile)
myPickler.dump(myList)
myFile.close( )

Question 9:
Illustrate shelving a dictionary.
Answer:
import shelve
myDict = {“name”: “John Doe”, “Address” : “1234 Main street”}
my Shelf = shelve.open(” my ShelveFile.dat”, “n”)
my Shelfl” Dictionary”] = myDict
my Shelf. close( )

Question 10:
Illustrate retrieving a dictionary from a shelf file Answer:
import shelve
myShelf= shelve.open(” my ShelveFile.dat”, “r”)
myDict = my Shelfl “Dictionary”]
myShelf.close( )

Question 11:
What Python module is used to work with a MySQL database?
Answer:
MySQLdb.

Question 12:
Illustrate connecting to a MySQL database on the local host, with a username and a password.
Answer:
import MySQLdb
myDB = MySQLdb.connect(host=”127.0.0.1″, user=”username”, passwd=”password”)

Question 13:
What is a MySQLdb cursor?
Answer:
It is a handle that lets you send SQL commands to MySQL and retrieve the result.

Question 14:
Illustrate creating a cursor, then sending a select
command to MySQL
Answer:
import MySQLdb
myDB = MySQLdb.connect(host=” 127.0.0.1″, usem”username”,
passwd= “password “)
my Cursor = myDB.cursorO
myCursor.execute(” select * from tablename”)
myResults = myCursor.fetchall( )

Question 15:
How are databases and tables created in MySQL from Python?
Answer:
Using a database connection and cursor to execute the appropriate CREATE DATABASE and CREATE TABLE SQL commands.

Question 6:
How is the currently selected database name retrieved?
Answer:
Using a database connection and cursor to execute a SELECT DATABASE() SQL command.

Question 17:
What is the .fetchall( ) method of the cursor object?
Answer:
It is used to retrieve all of the results of the executed SQL command. It returns a series of one or more lists depending on the results available.

Question 18:
Illustrate adding a row to a MySQL database
Answer:
import MySQLdb
myDB = MySQLdb.connect(host=”127.0.0.1”, user=”username”, passwd= “password “)
myCursor = myDB.cursor( )
my Cursor. execute(” INSERT INTO tablename VALUES ‘Vail’, ‘Val2’, ‘etc.'”)
myDB. commit( )

Question 19:
What does the .commit method of the database object do?
Answer:
It flushes the pending request buffer and ensures that the transactions are all written to disk.

Question 20:
Illustrate retrieving a list of available tables in a MySQL database.
Answer: „
import MySQLdb
myDB = MySQLdb.connect(host=” 127.0.0.1″, user=”username”, passwd= “password “)
myCursor = myDB.cursor ( )
myCursor.executeC’SHOW TABLES “)
myResults = myCursor.fetchall( )

Selenium Grid Tutorial: Hub & Node (with Example) | What is Selenium Grid?

Selenium Python – Selenium-Grid

Parallel execution of tests is made possible through Seleniumthrough its Grid component. We come across scenarios like executing tests for cross-browser verification or executing a huge test suite by splitting it into smaller suites in parallel to save time. For all these, the Grid component is useful and effective as it allows parallel test execution.

Structure

  • Selenium-Grid
  • Creating hub
  • Creating nodes
  • Executing tests in parallel

Objective

In this chapter, we learn how to s tup Selenium-Grid in our local system. We understand what is a hub and a node, and how we set them up. In this chapter, we will set up a hub that will act as a central server and receive requests. We will have a Chrome node and a
Firefox node to complete the Grid for execution.

Selenium-Grid

An important component of Selenium is the Selenium-Grid. It allows us to run our tests in parallel, which helps in saving time and cost. To set up the Selenium-Grid, we need to first download the Selenium standalone server from: https://www.seleniumhq.org/download/
After we have downloaded the server JAR file, we will store it in a folder. This JAR file can now be invoked in two different modes to set up the Grid:

  • The hub
  • The node

A hub is a central server that receives the request to execute the test. It will send the test to the node in the Grid which matches the description in the test. While anode is a machine and browser combination where the actual execution of the test takes place.
Let us see a Grid setup, with the help of the following diagram:

 

Selenium Python - Selenium-Grid chapter 12 img 1

In the above diagram, we can see that the hub will receive the test execution request, which will get passed on to the matching node for actual execution. As the test is executed at the node, the result is passed back to the hub. The information, about the browser, and the operating system on which the test is to be executed, is present in the test script which the hub receives. Then the test commands are sent to the matched node for the actual execution.

We will now set up a Grid with one hub and two nodes—one for Chrome and another for Firefox. Let us see the commands for it.

Setting up the hub

To set up the hub, we need to open the Command Prompt window and go to the folder where our standalone JAR file is present. There we need to type the following command:

D:\work\jarfile>java . jan selenium.server-standalone-3.141.59.jan-role hub

In the above command, we are executing the standalone JAR file using the role flag that uses the value hub. So it will execute the server in the hub mode. By default, it will start the hub on port 4444.
If we want to change the port, we can use the – port flag and provide a value to it. For example:

D:\work\jarfile>java . jan selenium-server-standalone-3.141.59.jan-role hub-port 6666

If you are working with a different Selenium standalone server version, the version number will change for you in here.
Once the hub has started, you can verify it using the following steps:

  1. Open a browser.
  2. Type this URL:
    http://localhost:4444/grid/console
  3. If all is working fine, you should be able to see the following:

 

Selenium Python - Selenium-Grid chapter 12 img 4

At the Command Prompt, we will see the following:

D:\work\Jarfiles>java - jar selenium-server-standalone-3.141.59.jar - role hub
17:17:17.540 INFO [GridLaunchV3.parse] - selenium server version: 3.141.59, revision: e82
be7d358
17:17:17.540 INFO [GridLaunchV3.lambda$buildLaunchers$5] - Launching selenium Grid hub on port: 4444
2019-05-27 17:17:18.139: INFO: : main : Logging initialized @1218ms to org.seleniumhq.jetty9.util.log.stdErrLog
17:17:18.576 INFO [Hub.start] - selenium Grid hub is up and running
17:17:18.576 INFO [Hub.start] - Nodes should register to http://192.168.0.109:4444/grid/register/
17:17:18.576 INFO [Hub.start] - Clients should connect to http://192.168.0.109:4444/wb/hub

Setting a Chrome node on a Windows machine

To set a Chrome node on a Windows machine, we will have to download the Selenium standalone server on that machine and execute it in the node mode. To do this, we will have to execute this command:

D:\work\JarFiles>java - Dwebdriver.chrome.driver="D:\work\JarFiles\resource\chromedriver.exe" - jar selenium-server-standalone-3.141.59-Jar-role - hub http://localhost:4444/grid/register - browser "browsername-chrome" - post5556

In this command, we are providing a path to the Chrome driver as per the location in our system, that is, Dwebdriver. chrome. driver=”D: \ WORK\DarFiles\nesource\chromedriver.exe”. Then we set the role flag to be the node. Then we provide the hub flag, where we point to the hub location: http://localhost:4444/grid/register. We set the browser flag to chrome and the port flag to 5556.
At the script level, we will be making some changes in the setup method, which is as follows:

def setup(self):
    self.driver = webdriver.Remote(
         command_executor="http://Localhost:4444/wd/hub",
         desired_capabilities={
              "browserName":"chrome",
     })
   self.base_url = "http://practice.bpbonline.com/catalog/index.php"

Here, we create an instance of the remote WebDriver, pass the details of the hub and in desired capabilities variable, we pass information for the browser, on which we want to execute our test, in this case, Chrome. We will discuss more desired capabilities a little later in the chapter. So when we execute the script, the commands are sent to the hub. It fetches the information to find the node on which the actual test is to be executed and sends the commands to it. In this case, it will send the commands to a node that is registered with a Chrome browser.

Please take a look at the following screenshot where we have entered all the details:

 

Selenium Python - Selenium-Grid chapter 12 img 8

Setting a Firefox node on a Windows machine

To set a Firefox node on a Windows machine, we will have to download the Selenium standalone server on that machine and execute it in the node mode. To do this we will have to execute this command:

D:\work\JarFiles>java - Dwebdriver.chrome.driver="D:\work\JarFiles\resource\getkodriver.exe" - jar selenium-server-standalone-3.141.59-Jar-role - hub http://localhost:4444/grid/register - browser "browsername-firebox" - post5557

In this command, we are providing the path to the gecko driver as per the location in our system: Dwebdriver. gecko. driver=”D: \WORK\ DarFiles\resource\geckodriver. exe”. Then we set the role flag to be a node. Then we provide the hub flag, where we point to the hub location: http: //localhost:4444/grid/register. We set the browser flag to firefox and the port flag to 5557.

At the script level, we will be making some changes in the setup method:

def setup(self):
    self.driver = webdriver.Remote(
         command_executor="http://Localhost:4444/wd/hub",
         desired_capabilities={
               "browserName":"firebox",
})
self.base_url = "http://practice.bpbonline.com/catalog/index.php"

Here, we create an instance of the remote WebDriver, pass the details of the hub and in desired capabilities variable, we pass information for the browser, on which we want to execute our test, in this case, the Firefox browser. We will discuss more desired capabilities a little later in the chapter. So when we execute the script, the commands are sent to the hub. It fetches the information to find the node on which the actual test is to be executed and sends the commands to it. In this case, it will send the commands to a node that is registered with a Firefox browser:

 

Selenium Python - Selenium-Grid chapter 12 img 11

Executing tests in parallel

To execute the tests in parallel, on the Chrome and Firefox browser together, we will initiate them. So the test execution commands will reach the hub, and the hub will direct them to their respective nodes. In this case, the tests are directed to Chrome browser machine, and Firefox browser machine:

 

Selenium Python - Selenium-Grid chapter 12 img 12

Desired capabilities

To set the test environment settings at the browser level, we have some properties associated with each browser that we can set. So at the time of execution, whenever the browser instance will be invoked it will be set with those browser properties. Internet Explorer, Chrome, and Firefox come with their own key-value pair.
In our above test scripts, we have used desired capabilities, to set the browser key-value to Firefox or Chrome, respectively:

desired_capabilities={
“browserName”: “firefox”.,}

desired_capabilities={
“browserName”: “chrome”,}

Conclusion

In this chapter, we understood the usage of an important component of Selenium which allows us to run tests in parallel. We understood how we establish the Selenium server in hub mode and node mode, and instantiate the remote Web’’)river at the script level to run our tests in the Grid.

Related Articles:

Locators in Selenium- How To Locate Elements On Web-page?

Selenium Python – Locators in Selenium

Introduction

When we try to automate a web application, there are two important steps to consider. One is to identify the object uniquely on which we would want to perform the action. The second is to perform the action on the identified object. To identify the object uniquely on the web page, Selenium provides us with some locator strategies. In this chapter, we will discuss and explore them.

Structure

  • What is a locator?
  • Different types of locators
  • Where are locators used?

Objective

When working with open source technology like Selenium, it is crucial for us to understand that as end-user what strategy Selenium uses to identify an object on a page. As we are going to write scripts to automate applications at hand, we will have to provide object information using one of the locator strategies which Selenium will use to identify the object on the page so that the required action can be performed on it.

What is a locator?

Locator is a technique to identify the object on a page uniquely by using different identification methods. Once the object is identified, the action can be performed on it. Selenium provides us with the following locator techniques to identify the object on the page:

  • ID
  • NAME
  • XPATH
  • CSS
  • DOM
  • LINKTEXT
  • PARTIALLINKTEXT

To understand the different strategies of the locators, which Selenium uses to identify the object on the page, we will take the example of an HTML code snippet from our web application: http://practice.bpbonline.com/catalog/index.php my account page, which we see,
when we click the My Account link of the home page. The idea is to explore the HTML code associated with the E-mail Address field,
Password field, and the Sign In button. So let us have a look at it:

Selenium Python - Locators in Selenium chapter 3 img 1

If we right-click the HTML page and select View page source. Refer to the following screenshot:

Selenium Python - Locators in Selenium chapter 3 img 2

We can see the HTML content associated with the web page. It is displayed as follows:

 

    <form name"login" action="http://practice.bpbonline.com/catalog/login.php"action=process" method="post"><input type="hidden"
name="formid" value="46fedd827e8b83241e4cebff3c6046ae"/>
   <table border="0" cellspecing="e" cellpadding="2" width"100%">
   <tr>
    <td class="filedkey">E-mail Address:</td>
    <td class="filedvalue"><input type="text" name="email..address"/><td>
 </tr>
 <tr>
   <td class="filedkey"password:</td>
   <td class="filedvalue"><input type="password" name="password" maxlength="40"></td>
</tr>
</table>

<P><a href="http://practice.bpbonline.com/catalog/password_forgotten.php">password forgotten? click here.</a></p>

<p> align="right"><span class="tdblink"><button id="tdb1" type="submit">sign In</button></span><script type="text/javascript $("#tdb1").button({icons:{primary::ui.icon-key"}}).addclass("ui-priority-primary").parent( ).removeClass("tdblink"):
</script></p>

</form>

As we have seen, the above HTML content is associated with three objects – username and password fields, and sign-in button, let us try to understand the different locator strategies Selenium can use to identify them so that an action can be performed on them as our automation script executes. So, the following is explained below:

• ID: The ID locator is fetched from the ID attribute of an HTML element. If the HTML element of interest has an ID attribute, we use it to identify the object uniquely. The example of this is the Sign In button, which has the id = tdblxbutton id=”tdbl” type=”submit”>Sign In</button>

• NAME: This attribute is fetched from the NAME attribute of the HTML element. The data associated with this property of the HTML element is used to identify the object uniquely on the web page. Examples of this property username, and password fields:
<input type=”text” name=”email_address” />
<input type=”password” name=”password” maxlength=”40″ />

• XPATH: The path traversed to reach the node of interest in an XML document is known as XPATH. To create the XPATH locator for an element, we look at an HTML document as if it is an XML document, and then traverse the path to reach it. The XPATH can either be a relative or an absolute one:

  • A relative XPATH will be in relation to a landmark node. A node that has a strong identifier like an ID or NAME. It uses / / in its path creation. Example: // input[@name=”email_addpess”]
  • An absolute XPATH starts from the root node HTML. It uses a single slash /. It is more prone to changes if the document structure undergoes changes during the development of the application, so it is generally avoided.
    Example:/HTML/body/div[2] / f orm[0] /table/ tbody/tr[2]/input

• CSS: It stands for Cascading Style Sheets. We can use this as well to identify the objects uniquely on the web page. The syntax is as follows:

  • If the HTML of the object has an ID attribute then, css=#ID, for example, css=#tdbl
  • Else, css=HTMLtag[prop=value], for example, css=input [namie^ email_address’ ]

• DOM: It stands for Document Object Model. It allows object identification by using the HTML tag name associated with the object.

• LINKTEXT: Generally, whenever we encounter a link in the application we can use it to identify the object on the page. For example, the My Account link can be identified using the same link text as seen on the web page

• PARTIAL LINK TEXT: We can also use a subpart of a complete text of the link to identify it on the web page and then perform actions on it.

It is important to use an appropriate locator to identify the object on the page, which is unique, helps in quick identification of the object, and is robust to application changes during the development process. Generally, if the object HTML has IDor NAME we use it to identify the object on the page. Then we use XPATH, followed by CSS and the last option is DOM. If it is a link, we always use LINKTEXT or PARTIAL LINK TEXT to identify the element on the page. So ideally, this should be the approach we need to take.

Conclusion

In this chapter we discussed the concept of locators; we understood its various types. We also saw where and how they would be needed. These locator strategies are standard in Selenium. They are not going to get modified in any version, and have been consistent with old versions as well. Lastly, we need to keep in mind that the locator strategy we are choosing to identify the object has to be robust and help to locate the object quickly on the page. In the next chapter, we will learn the steps to set up Selenium and Eclipse IDE on Windows OS.

Related Articles:

Synchronization in Selenium Python | Synchronizing Test

Synchronization in Selenium Python

Selenium Python – Synchronizing Test

In our earlier chapters, we have learned how do we automate scenarios, identify objects uniquely on a web page and perform actions on them. During the automation of scenarios, it is important that we synchronize our tests. So the time it takes for the web application to process, the automation command should match with the speed at which the command is sent by the script to the application.

Structure

  • Synchronizing test
  • Why synchronization
  • Implicit wait
  • Explicit wait

Objective

In this chapter, we will leam how we can establish synchronization in our tests so that we ensure our tests are not flaky. Reliable execution is crucial during testing cycles as they help save time, and ensure reliability in test automation exercises. We will learn how we implement that concept in our test scripts.

Synchronization

If we have not implemented synchronization in our tests, then our tests may hover between passing and failing, resulting in flaky tests. To avoid this we should synchronize our tests so that we have basic reliability in our test behavior. To achieve it we have to apply synchronization in our tests.
There are two kinds of synchronization techniques that we use in test scenarios of Selenium:

  • Implicit wait
  • Explicit wait

Implicit wait

The implicit wait is a global wait, which applies to every statement in the written test script. An implicit wait, when implemented in the script, tries to find the element on the web page. It will keep polling the web page until the element is found, or till the time is over. If the element is not found within the provided implicit wait, we get an exception NoSuchElementException.

In the following program, we will implement implicit wait:

from selenium import webdriver
import unittest

class Login(unittest.Testcase):
      def setup(self):
          self.driver = webdriver.chrome(executable_path="D:\Eclipse\BPB\seleniumpython\seleniumpyhon\drivers\chromedriver.exe")
          self.base_url = "http://practice.bpbonline.com/catalog/index.php"

def test_login(self):
    driver=self.driver
    driver.get(self.base_url)
    driver.find_element_by_link_test("My Account").click( )
    driver.find_element_by_name("email_address").clear( )
    driver.find_element_by_name("email_address").send_keys("bpb@bpb.com)
    driver.find_element_by_name("password").clear( )
    driver.find_element_by_name("password").send_keys("bpb@123")
    driver.find_element_by_id("tab1").click( )
    driver.find_element_by_lik_text("log off").click( )
    driver.find_element_by_lik_text("continue").click( )

def tearDown(self):
    self.driver.quit( )

If_name_==_main_":
   unittest.main( )

The commanding self .driven.implicity_wait(30) will apply the wait on every find element command used in the program. So for every statement, it will wait for 30 seconds for the object to appear with the given By locator. It will keep polling the website until it finds the object. If the object is found, the action will be performed on the object. Else after the 30 seconds are over, we will get an exception.

Explicit wait

The explicit wait is basically a local wait which can be implemented either as:

• Static wait: It is a forced wait, which is introduced by using time.sleep(n seconds) in the code line, whenever we wish to wait in the code for n number of seconds. It is not advisable to use static wait in the code because we generally do not know if the time allocated to wait is less or more. We cannot provide a lot of time to wait in the code, because that will delay our test automation script, and if we provide very little time, it may result in a flaky test, so such waits are generally unreliable and not advisable.

• Dynamic wait: The dynamic wait is implemented in the code with the help of a class called as WebDriverWait. This class has a method called as until ( ). In this method, we pass an event which may occur and the time units for which we wish to wait for that event to occur. So in this method of WebDriverWait, we either wait for an event to occur or it times out. The exception we get in here, in case the event doesn’t occur, is TimedOutException. But in case the event has occurred, we do not wait for the entire amount of time to finish, we get out of the until loop as soon as it’s finished.

So, we will have a look at two sets of code here. In the first example, we will run a positive scenario of login logout using WebDriverWait. In the second example, we will execute a negative scenario in which we will fail forcefully by passing wrong object information in the code. So the method will wait for the timeout object to appear:

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import webDriverwait
from selenium.webdriver.support import expected_conditions as EC
import unitest

class Login(unitest.TestCase):
      def setup(self):
          self.driver = webdriver.chrome(executable_path="D:\Eclipse\BPB\seleniumpython\seleniumpython\drivers\chromedriver.exe")
          self.base_url="http://practice.bpbonline.com/catalog/index.php"

dex test_login(self):
    driver=self.driver
    driver.get(self.base_url)
    driver.find_element_by_link_text("My Account").click( )
    driver.find_element_by_name("email_address").clear( )
    driver.find_element_by_name("email_address").send_keys("bpb@bpb.com")
    driver.find_element_by_name("password").clear( )
    driver.find_element_by_name("password").send_keys("bpb@123")
    driver.find_element_by_id("tab1").click( )
    webdriverwait(driver, 1e).until(
    Ec.presence_of_element_located((By.LINK_TEXT, "log off")))
    driver.find_element_by_link_text("log off").click( )
    driver.find_element_by_link_text("continue").click( )

def tearDown(self):
    self.driver.quit( )

if _name_ ==" _main_ ":
    unitest.main( )

The code lines using which we have implemented explicit wait are: WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.LINK_TEXT, “Log Off”)))

In the preceding until( ) method, we take an input argument called EC. presence_of_element_located(By.LINK_TEXT, ” Log Off”), here the EC is basically the class called as expected_ conditions exported from selenium.web driver.support import expected_conditions asEC. It has a lot of methods available with it which can help trace an event. In the preceding code, we have used a method presence_of_element_located, so this will basically look for the link with the text Log Off, for 10 seconds. If it finds the link within 10 seconds it will exit the until loop and execute the next command, which is clicking on the link, otherwise, it will timeout and throw a TimedOutException.

Let us try another code example, where we will give a bad locator for the Log Off link, causing the WebDriver wait method to timeout:

WebOriverHait(driver, 10}.until(
EC:presence_of_elenent_located((By.LIHK_TEXT, “Log ££”})) driver.find_eleinent_by_link_text(“Log Off”).click()

The exception which is thrown is an follows:
raise TiraeoutException(message, screen., stacktrace)
selenium.common,exceptions.TimeoutrExceptions Message;

Conclusion

In this chapter, we have seen how we can use the concept of waits, implicit and explicit wait, in our test automation code and ensure that the scripts are reliable during execution time. By implementing the concept of synchronization we have tried to achieve less flaky tests and introduce predictability in them during execution time. In the next chapter, we will discuss advanced types of web elements like table, dropdown, alerts, and frame. We will see how to automate these elements and what all methods are associated with them.

Related Articles:

Page Object Model in Selenium Python | POM in Selenium Python

Page Object Model in Selenium Python | POM in Selenium Python

Selenium Python – Page Object Model

Selenium is an open-source test automation tool. Like other commercial tools in the market, it doesn’t come up with an inbuilt feature to manage object information which we use in creating test scripts. So we need to take the help of design patterns like POM to manage these artifacts. In this chapter, we will understand how to create them and what benefits they bring to the table.

Structure

  • Page Object Model (POM)
  • Implementing the POM • Example of login logout scenario

Objective

This chapter will help us understand the concept of a design pattern called Page Object Model (POM), and how to implement it to make our scripts more robust.

Page Object Model (POM)

Page Object Model (POM) is a design pattern that helps us to separate the object information from the business logic of a page from the application. The idea behind this is, if the object information changes from one build release to another, the business logic is not impacted at the code level, and we only make changes at the object information level. To achieve this we need to implement a POM design pattern at our code level.

Creating Selenium test cases can result in an unmaintainable project. One of the reasons is that too much-duplicated code is used. Duplicated code could be caused by duplicated functionality and this will result in duplicated usage of locator information to identify the objects on a page. The disadvantage of duplicated code is that the project is less maintainable. If some locator will change, you have to walk through the whole test code to adjust locators where necessary.

Implementing the POM

By using the POM we can make non-brittle test code and reduce or eliminate duplicate test code. Besides, it improves readability and allows us to create interactive documentation. Last but not least, we can create tests with fewer keystrokes.
The concept of POM says that when we look at a page, we should see if it has got two components:

  • Objects
  • Business logic

So the page has a business logic, and to achieve that business objective there are objects available on the page. We need to segregate these two entities. The reason for this is, that over a period of time as an application undergoes changes the object information can get changed more frequently, making the maintenance of the code a humongous effort. Let us take an example of the My Account Page here from our application: http://practice.bpbonline.com/catalog/login.php
The business logic of the page says:

  • If there exists a new user, allow them to create a new account.
  • If there is a registered user with valid credentials allows them to log in.
  • If there is a registered user, but who has forgotten credentials, allow them to fetch the password.
  • Not allow a user with invalid credentials to log in.

To achieve these business functions, the page is designed with the following objects:

  • Username textbox
  • Password textbox
  • Sign-in button
  • Continue button

Please find the following screenshot explaining the preceding bullet points:

Selenium Python - Page Object Model chapter 11 img 1

By keeping object information separate from business logic, we are able to manage and achieve modularity and robustness at the code level. As we find during build release cycles, the object information may change over a period of time, so only the files where object information is stored need to be changed, whereas the test logic which is consuming these objects will not get affected by it.

In this chapter, we will see how we will implement POM for the login logout scenario of our application.

from selenium.webdriver.common.by import By

# for maintainbility We can seperate web objects by page name

class mainpageLocators(object):
MYACCOUT    = (By.LINK_TEXT, 'My Account')

class LoginpageLocators(object):
Email       = (By.NAME,   'email_address')
PASSWORD    = (By.NAME,   'password')
SIGNIN      = (By.ID,   'tab1')


class LogoutpageLocators(object):
LOGOFF     = (By.LINK_TEXT, 'log off')
continue   = (By.LINK_TEXT, 'continue')

So, if the object information in any of the upcoming builds changes, we can come to this file, and change the information associated with that object. In another file, called as pages. py we call the action associated with each object:

class Mainpage(page):
    def click_myaccount(self):
        self.find_element(*mainpageLocators.MYACCOUNT).click( )
        return self.driver

class Loginpage(page):
     def enter_email(self, email):
        self.find_element(*LoginpageLocators.EMAIL).send_keys(email)

def enter_password (self,pwd):
    self.find_element(*LoginpageLocators.PASSWORD).send_keys(pwd)

def click_login_button(self):
    self.find_element(*LoginpageLocators.SIGNIN).click( )

def login(self, email,pwd):
    self.enter_email(email)
    self.enter_password(pwd)
    self.click_login_button( )
    return self.driver


class Logoutpage(page):
   def click_logoff(self):
       self.find_element(*LogoutpageLocators.LOGOFF).click()

def click_continue(self):
     self.find_element(*LogoutpageLocators.CONTINUE).click( )

def logout(self):
    self.click_logoff( )
    self.click_continue( )

Now, we will create the test scenarios:

def test_sign_in_with_valid_user(self):
mainpage = mainpage(self . driver)
mainpage . Click_myaccount( )
loginpage=loginpage(self.driver)
loginpage . login("bpb@bpb.com","bpb@123")
logoutpage=logoutpage(self.driver)
logoutpage . logout ( )

As we can see, our actual test scenario looks clean, easy to read. Since object information is now hidden in the other files, the test can concentrate on actual test logic.

Conclusion

In this chapter, we learned the importance and need for POM to manage and maintain object information in the form of page objects. This is necessary as Selenium by default doesn’t come with any feature like object repository to manage and maintain object information. POM helps us create modular and robust code.
In our next chapter, we will discuss the concept of Selenium Grid, which as a component, allows us to execute scenarios in parallel.

Related Articles: