Learn Python – Stack in Python- Basic and advance

In this tutorial, we will analyze the basics of the stack and enforce it using the Python code.

What is a Stack?

A stack is a linear information structure the place statistics is arranged objects on over another. It stores the records in LIFO (Last in First Out) manner. The data is stored in a similar order as plates are arranged one above some other in the kitchen. The easy instance of a stack is the Undo feature in the editor. The Undo characteristic works on the last event that we have done.

We continually choose the ultimate plate from the stack of the plate. In stack, the new component is inserted at the one stop and an thing can be removed only that end.

We can function the two operations in the stack – PUSH and POP. The PUSH operation is when we add an issue and the POP operation is when we cast off an issue from the stack.

Methods of Stack

Python gives the following techniques that are generally used with the stack.

empty() – It returns true, it the stack is empty. The time complexity is O(1).

size() – It returns the length of the stack. The time complexity is O(1).

top() – This method returns an address of the last element of the stack. The time complexity is O(1).

push(g) – This method adds the element ‘g’ at the end of the stack – The time complexity is O(1).

pop() – This method removes the topmost element of the stack. The time complexity is O(1).

Implementation of Stack

Python offers a variety of ways to enforce the stack. In this section, we will talk about the implementation of the stack the use of Python and its module.

We can enforce a stack in Python in the following ways.

List

dequeu

LifeQueue

Implementation Using List

Python list can be used as the stack. It makes use of the append() approach to insert elements to the listing where stack uses the push() method. The listing also offers the pop() technique to cast off the closing element, however there are shortcomings in the list. The listing will become gradual as it grows.

The list stores the new thing in the subsequent to other. If the listing grows and out of a block of memory then Python allocates some memory. That’s why the list grow to be slow. Let’s understand the following instance –

# initial empty stack  
my_stack = []  
  
# append() function to push   
# element in the my_stack   
my_stack.append('x')  
my_stack.append('y')  
my_stack.append('z')  
  
  
print(my_stack)  
  
# pop() function to pop   
# element from my_stack in    
# LIFO order   
print('\nElements poped from my_stack:')  
print(my_stack.pop())  
print(my_stack.pop())  
print(my_stack.pop())  
  
print('\nmy_stack after elements are poped:')  
print(my_stack)   

Output:

['x', 'y', 'z']

Elements poped from my_stack:
z
y
x  

my_stack after elements are poped:
[]
Traceback (most recent call last):
  File "C:/Users/DEVANSH SHARMA/PycharmProjects/Hello/Stack.py", line 21, in <module>
    print(my_stack.pop())
IndexError: pop from empty list

Explanation –

In the above code, we have described an empty list. We inserted the factors one by one the use of the append() method. That is comparable to the push() method. We additionally removed the factors the use of the pop() method. The pop() approach returns the closing thing of the list.

Implementation using collection.deque

The collection module offers the deque class, which is used to creating Python stacks. The deque is mentioned as the “deck” which potential “double-ended queue”. The deque can be favored over the list because it performs append and pop operation quicker than the list. The time complexity is O(1), the place the listing takes O(n).

Let’s understand the following example –

Example

from collections import deque  
  
my_stack = deque()  
  
# append() function is used to push   
# element in the my_stack   
my_stack.append('a')  
my_stack.append('b')  
my_stack.append('c')  
  
print('Initial my_stack:')  
print(my_stack)  
  
# pop() function is used to pop   
# element from my_stack in    
# LIFO order   
print('\nElements poped from my_stack:')  
print(my_stack.pop())  
print(my_stack.pop())  
print(my_stack.pop())  
  
print('\nmy_stack after elements are poped:')  
print(my_stack)   

Output:

Initial my_stack:
deque(['a', 'b', 'c'])
Elements poped from my_stack:
c
b
a
my_stack after elements are poped:
deque([])

Explanation:

The above code is almost similar to the previous example. However, the only difference is that, we have imported the deque from the collection module.

Deque Vs. list

The list stores the factor right subsequent to every different and uses the contiguous memory. This is most wonderful for the various operations, like indexing into the list. For example – Getting list1[2] is fast as Python is aware of the precise role of a particular element. The contiguous reminiscence additionally lets in slice to work well on the lists.

The listing consumes greater time to append() some objects than others. If the block of contiguous reminiscence is full, it will get another block that can take lots a longer time than a ordinary append() function.

Implementation Using queue module

The queue module has the LIFO queue, which is the identical as the stack. Generally, the queue makes use of the put() technique to add the data and the () approach to take the data.

Below are a few techniques that available in the queue.

empty() – If queue empty, returns true; otherwise return false.

maxsize() – This method is used to set the maximum number of elements allowed in the queue.

get() – It returns and removes the element from the queue. Sometime. The queue can be empty; it waits until element is available.

full() – It returns True if the queue is full. The queue is defined as maxsize = 0 by default. In this case, it will not return True.

put(item) – It adds the element to the queue; if the queue is full, wait until space is available.

put_nowait(item) – It adds the element into the queue without delaying.

qsize() – It returns the size of the queue.

Let’s recognize the following example of the queue module.

Example –

# Implementing stack using the queue module  
from queue import LifoQueue   
  
# Initializing a my_stack stack with maxsize  
my_stack = LifoQueue(maxsize = 5)  
  
# qsize() display the number of elements  
# in the my_stack   
print(my_stack.qsize())   
  
# put() function is used to push  
# element in the my_stack   
my_stack.put('x')  
my_stack.put('y')  
my_stack.put('z')  
  
print("Stack is Full: ", my_stack.full())   
print("Size of Stack: ", my_stack.qsize())   
  
# To pop the element we used get() function  
# from my_stack in   
# LIFO order   
print('\nElements poped from the my_stack')   
print(my_stack.get())   
print(my_stack.get())   
print(my_stack.get())   
  
print("\nStack is Empty: ", my_stack.empty())  

Output:

0
Stack is Full:  False
Size of Stack:  3

Elements poped from the my_stack
z
y
x

Stack is Empty:  True

Explanation –

In the above, we have imported the queue module that is a LIFOqueue. It works the identical as the stack however this module consists of some additional features referred to above. We described a stack with the maxsize that capacity it can maintain most five values in it.

The preliminary array size is zero; we pushed three elements in the stack using the put() method. Now, again we checked whether a stack is empty and size of the stack. We have three factors in the stack. We popped the thing the usage of the get() method. It gets rid of the closing added thing first. After eliminating the entire elements, we get an empty stack.

Python Stacks and Threading

We can additionally use Python stack in the multi-threaded program. It is an advanced subject but now not frequently used so you can bypass this section if you are no longer involved in threading.

The list and deque behave in a different way if we are the use of threading in our program. Using a list in multi-threading programming can be unsafe because it is no longer thread-safe.

On the different hand, the deque is a little bit complicated due to the fact its append() and pop() strategies are atomic, which potential they will no longer interrupt with the aid of other thread.

We can construct the multithreading program the usage of the deque, but it can also cause few complexities in the future.

Now the question arises, how we can construct a application of Python stack with threading.

The reply is that we can use the LIFOqueue and we comprehend that what LIFO stands for – Last In First Out. It uses the put() and gets() to add and get rid of the stack element.

Which Implementation of Stack Should Consider?

We have mentioned three strategies of enforce the stack in Python. The above techniques have their very own benefits or disadvantages.

Let us cleat the confusion; we are the use of stack with the threading, you should use the Lifoqueue but make sure about its overall performance for popping and pushing elements. But if you are no longer the usage of threading, use a deque.

We can also use the listing to put in force the stack but the listing can have viable memory reallocation issues. The list and deque are identical in the interface, but the deque would not memory allocation issues.

Conclusion

We have described a stack and its implementation in brief. Python stack can be used in real-life programs. We can select both put in force the technique in accordance to our requirements. We have also defined the Python stack with threading environment.