Python Interview Revision Notes, Only Those Topics That You Most Likely Missed 🤔

Ganesh Prasad
8 min readDec 29, 2022

--

I recently gave an interview for a python developer, and for the most part, let’s say 60%, it went well. But as the interview progressed further and further, I started getting questions that I didn’t revise enough and didn’t require during my job searching period.

Still, I had some idea about those topics, so I managed it. And, I got the job.

I am writing this article, but treat it as a revision note or a guide for which topics you should brush up on before you attempt an interview based on python.

Serialization and Deserialization

  • Serialization: from the pickle module, use pickle.dump() which takes the file name and the object (instantiated from a class).
  • Deserialization: from pickle, use pickle.load() that takes the file and returns the object

File handling

open() to open file with modes such as (w (for writing), w+(for writing and reading), r (for reading), r+, a (for appending), a+)

close() when you don’t need the file.

seek(takes cursor position and moves the cursor to that location)

file.tell() tells the current cursor position.

Exception handling

try: block contains the code that can cause an error

except: contains code to handle the exception

else: code that runs if no exception is encountered

finally: closing connections( DB, file, etc)

assert condition, error message — causes the AssertionError exception, used to check some condition

raise is used to raise an exception

Abstraction and interfacing

How to make an abstract class and an interface in python

To create abstract classes, import from abc {abstract base class} module

to make a class abstract, make it extend ABC and decorate the abstract method with @abstractmethod

from abc import abstractmethod, ABC
class CAR(ABC):
def __init__(self):
#do something
pass

# abstract method
@abstractmethod
def drive(self):
pass

def horn(self):
print("making sound!")

INTERFACE

make all the methods of the class abstract to make a class an interface

from abc import abstractmethod, ABC

# interface (make all the methods, abstract)
class CAR(ABC):
def __init__(self):
#do something
pass

# abstract method
@abstractmethod
def drive(self):
pass

@abstractmethod
def horn(self):
pass

Generators

the method used to create custom sequences of data returns a value using yield that can save their states and resume from where they had stopped.

simple example

def generateUpto5():
i = 1
while i<=5:
yield(i)
i+=1
# call the function multiple times

generateUpto5() //returns 1
generateUpto5() //returns 2

Decorators

used to decorate methods, used to add some extra information to the output of a function

# defining a decorator
def decorAdd5(func):
def inner():
result = func()
result += 5
return result
return inner


# decorating a function
@decorAdd5
def returns1():
return 1

It is possible to apply multiple decorators.

@decorator1
@decorator2
def returns1():
return 1

Arguments

*args: to pass optional arguments to a function

**kwargs: to pass keyword optional arguments

any number of extra arguments

GLOBAL to access global variables

globals() returns a dictionary of global variables

Command line arguments

import sys

sys.argv is a list that contains all the arguments that is passed to the program

Lambdas

simple functions with one return statement

func = lambda x: x+1

using filter, map, reduce

Regular expressions

import re [stands for regular expressions]

match, returns a matching string object. result.group() needs to be called for the result

search returns matching string result when result.group() is called,

findall returns a list of all matched strings,

split takes a regex which is used as a delimiter to split the string,

sub stands for substitute: ,

\d for any digit [single character]

\D for any non digit

\s for any white space

\S for any non white space

\w for any alpha numeric character

\W for any non alpha numeric character

\b space around words

\A start of the string

\Z end of the string

{} occurance of the character

Special character

\ escape character used to escape

. any one character

^ beginning of a string

$ end of the string

[…] range of values

[^…] matches every character other than the range provided

() to pass regex

(R|S) to give two regex, match either of the two.

Date and time

epoch: seconds since a certain timestamp (jan 1970)

import time

time.time() returns the epoch seconds

time.ctime(seconds) returns time and date as a string

Thu Dec 29 14:32:24 2022

FOR Performance benchmarking

use perf_counter() from time module: returns time in seconds

call it in the begining and then at the end. subtract to get the difference or seconds elapsed.

Threading

I will add a few simple examples for each case in 1 or 2 days.

for threading, we use the threading module

import threading

2 ways to create a thread:

  1. using the Thread() constructor and passing functions along with the arguments that the function uses. The created thread object is started using start() method.
  2. Extending the Thread class and overloading the run method and then the thread object is started using the start method.

Thread synchronization

  • Mutex: locking an object so that only one thread can use it.

One thread locks an object using lock(), then acquire(), then release().

  • Semaphore: has a number showing maximum threads allowed, each thread makes it reduce it by one, when a thread releases the object

Locking:

Assign a lock object đź”’ to an object. In the method, make the thread call acquire() then the thread acquiring the lock is the sole owner of the object. once work is done release the object using release() method of lock.

Semaphores:

same as lock just use the constructor Semaphore instead of Lock’s constructor.

Communication between threads:

Often one thread creates, and some other thread consumes,

Producer and Consumer.

The producer thread produces, and The Consumer thread consumes.

The consumer keeps polling to check if a flag in the Producer is set.

from threading import *
from time import *
# producer class
# produces a cart
class Producer:
# constructor
def __init__(self) -> None:
self.products = []
self.productsAvailable = False
# function to produce def produce(self):
for i in range(1, 5):
self.products.append("Products" + str(i))
print("product added!")
sleep(1)
self.productsAvailable = True
# defining Consumer
class Consumer:
# constructor
def __init__(self, producer):
self.producer = producer
# consume
def consume(self):
# polling
while self.producer.productsAvailable == False:
print("waiting...")
sleep(0.2)
print("products added: ", self.producer.products)# create the two threads
producer = Producer()
consumer = Consumer(producer)
p = Thread(target=producer.produce)
c = Thread(target=consumer.consume)
p.start()
c.start()

Wait & Notify

producer has a condition class and the consumer calls the wait() command to wait and when the producer calls the notify() (for one thread) or notifyall() (for all the thread)

Let’s see how to use this method:

  1. Assign a Condition object (lock đź”’) to the producer.
  2. In the producer’s produce method, call acquire() on lock, do any producing work. Once the work is done, call notify() to notify the waiting thread and call release.
  3. In the consumer thread’s consume method, call acquire() on the lock of the producer object that is avaialble to the consumer object. And then call wait() with a timeout value passed.
  4. Finally call release method() on the lock object in the consume.

The Code example for the notify and wait:

Some changes like no boolean flag needed, a lock object is enough.

from threading import *
from time import *
# producer class
# produces a cart
class Producer:
# constructor
def __init__(self) -> None:
self.products = []
self.c = Condition()
# function to produce def produce(self):
# acquire the lock
self.c.acquire()
for i in range(1, 5):
self.products.append("Products" + str(i))
print("product added!")
sleep(1)
# notify all the waiting threads
self.c.notify()
# release the lock
self.c.release()
# defining Consumer
class Consumer:
# constructor
def __init__(self, producer):
self.producer = producer
# consume
def consume(self):
# acquire the lock of the producer
self.producer.c.acquire()
# now wait for the producer to call notify
self.producer.c.wait(timeout=0)
self.producer.c.release()
print("products added: ", self.producer.products)
# create the two threads
producer = Producer()
consumer = Consumer(producer)
p = Thread(target=producer.produce)
c = Thread(target=consumer.consume)
p.start()
c.start()

Queue and Thread communication

Queue class has two methods get and put. Both lock the queue while one is executing.

Hence, syncronization is taken care of.

Producer puts work done on a queue. Same queue is shared with consumer. Which then calls get() to get the data.

Producer and Consumer pattern using queue

get() doesn’t give an error, it just waits.

A simple example for sync and communication using queue:

from threading import *
from time import *
from queue import Queue
import random
# create a producer method
# that accepts a queue as parameter
def producer(q):
# run an infinite loop that produces data
while True:
print("producing...")
q.put(random.randint(1, 30))
sleep(3)
# create a consumer method
def consumer(q):
# keep asking for data
while True:
print("ready and waiting...")
print("consume data: ", q.get())
sleep(3)
# create a queue
q = Queue()
# create two threads
p = Thread(target=producer, args=(q,))
c = Thread(target=consumer, args=(q,))
p.start()
c.start()

Types of Queues:

  • By default, queue.Queue is FIFO, ie, 1, 2, 3, 4 leads to 1, 2, 3, 4
  • queue.LifoQueue is LIFO (stack), 1, 2, 3, 4 leads to 4, 3, 2, 1
  • queue.PriorityQueue uses a priority to order, in the case of numeric values,

How to Add non numeric value to Priority Queue. Just put a tuple with first value being a numeric value and second any type.

Networking

url and others — urllib (library)

socket programming — socket (library)

email — smtplib (library)

  • For url (html): urllib.request.urlopen(”url”) throws HTTPError; gives a url object; call read on the object and close.
  • For image: urllib.request.urlretrieve(”url”, “filenameToHoldData”)

Socket

Server:

  • create a socket: socket.socket() takes protocol version (ipv4/ipv6) and connection type (tcp or udp)
  • bind to a host: socketObj.bind()
  • listen: socketobj.listen() → it waits for a connection request, and resumes once a connection has arrived.
  • then call socketObj.accept() returns a connection object and an address object.
  • send messages using the connection object, encode the message. (b”message” or “message”.encode())
  • and finally close() the connection object.

Client:

  • create a socket
  • call connect on socket object with host name and port number.
  • call recv(buffersize(number of bytes))
  • use a loop to recieve all the message till size of recieved packet is 0.
  • The message should be decoded.
  • call the close() on connection when done.

File Server:

use the usual send and recv to send file data.

Email Server: (due to new google rules this is deprecated)

There are other methods though.

  • library smtplib used to connect to an smtp server
  • library email.mime.text module using MIMEText, this gives a mail body object with all the fields like body, subject, to and from
  • use smtplib’s SMTP call to create an email server object; accepts server address and port

smtp.gmail.com and 587

  • call starttls() to start a tls connection
  • call login() with email id and password to login
  • send() with mail object
  • quit() to close the server

Database connections (structured)

  • install the connector (pip3 install mysql-connector-python)
  • import the module above (mysql.connector)
  • establish a connection (call connect()) returns the connection object. Takes 4 parameters, host name, database name, user and password.
  • get a cursor used to make insert and others operation.
  • cursor’s execute is used to make query, cursor object stores the data.
  • Fetch, fetchall, etc can be used to get the data from cursor. (as a tuple or list of tuples)
  • close() on connection and cursor to close the db connection.

Sample code:

import mysql.connector
# get a connection object
conn = mysql.connector.connect(
host="localhost", database="mydb", user="root", password="tiger")
# get a cursor object
cursor = conn.cursor()
if conn.is_connected():
print("connection is made.")
'''
emp has three columns; id, name, salary
'''
# execute a query
cursor.execute("select * from emp")
row = cursor.fetchone()while row is not None:
print(row)
row = cursor.fetchone()
cursor.close()
conn.close()

Creating rows and deleting:

  • execute(”insert into or delete from query”)
  • need to commit on connection to make the final change
  • wrap the execute and commit() in a try except block.
  • if anything goes wrong call rollback() on cursor object (in except block)

The End

I will add example codes and a few more topics in 1 or 2 days.

During the interview, I forgot the four pillars of OOPS. I know you know, I did too, but I couldn’t remember the names. So, google them if you can’t recall.

Good luck and take care. Once it is completed, I will reformat this article into a beautiful one.

--

--

Ganesh Prasad
Ganesh Prasad

Written by Ganesh Prasad

Backend Developer at Appscrip | C++ veteran, đź’ś Dart

No responses yet