Python Interview Revision Notes, Only Those Topics That You Most Likely Missed 🤔
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:
- using the Thread() constructor and passing functions along with the arguments that the function uses. The created thread object is started using start() method.
- 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 cartclass 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:
- Assign a Condition object (lock đź”’) to the producer.
- 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.
- 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.
- 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 cartclass 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 parameterdef 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.