Python Interview Questions

26.07.2024

cover

If you write code in Python and enjoy working with this language, then this post is for you. Let’s have an interview and assess how well you understand Python.

Interview meme

Question 1

Question:
Explain why the following code behaves the way it does:

>>> a = 256
>>> b = 256
>>> a is b
True
>>> x = 257
>>> y = 257
>>> x is y
False

Answer:
Python caches numbers in the range of -5 to 256 source.
You can learn more about memory management in Python by reading the Python Memory Management post.

Question 2

Question:
How does import work in Python? Why does the following code return the results it does?

>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3
>>> math.pi
3
>>> import math
>>> math.pi
3

Answer:
Python frozen modules ensure that the Python interpreter loads a module into the sys.modules dictionary only once and reuses it afterward. To see this in action, open a Python3 interactive shell (open a terminal, type python3, and hit enter), and try importing the __hello__ module:

>>> import __hello__
Hello world!
>>> import __hello__
>>>

Question 3

Question:
What is __pycache__, and what are .pyc files?

Answer:
Whenever you run a Python program, the interpreter compiles your code to bytecode (for efficiency) and stores it in a folder named __pycache__ [source].

If you look inside this folder, you’ll see .pyc or .pyo files. Rather than reading and executing the code line by line, the Python interpreter executes the cached .pyc bytecode in the virtual machine. This process is similar to Java.

Question 4

Question:
What impact does exception handling have on the performance of a program? If it slows down the program, how would you minimize this slowdown?

Answer:
Python uses a stack for exception handling. When an exception occurs, the interpreter looks for the exception handler in the call stack (your try-except code). If no handler is found, Python terminates the program and prints a traceback in the terminal.

Python encourages the use of EAFP ("it's easier to ask for forgiveness than permission") over LBYL ("look before you leap") style coding. To write efficient and simple code, consider a scenario where users provide a list of numbers [1, 2, 3, 4], and the program calculates the sum. If you are confident that users will input numeric lists 90% of the time, you can use try-except. If not, it’s better to use an if-else statement to handle the control flow (based on personal experience and advice from Core Python Developers).

Question 5

Question:
Why does the following code return False? Both objects have the same value. How would you correct it?

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None

x = Node(1)
y = Node(1)

x == y # False

Answer:
There are two solutions for this.

Solution 1: Using dunder methods. You can learn more by following the link.

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None

    # The equal dunder method is used for the == operator.
    # If self is Node(1) and other is also Node(1), it compares their val attributes.
    def __eq__(self, other):
        return self.val == other.val

x = Node(1)
y = Node(1)

x == y # True

Solution 2: The dataclasses module is useful here. If you don’t want to write all the dunder methods yourself, you can use the dataclass decorator, and it will automatically generate the necessary methods for you.

from dataclasses import dataclass

@dataclass
class Node:
    val: any
    next: None = None  # Default is None, but it can be changed.

x = Node(1)
y = Node(1)

x == y # True

Question 6

Question:
What is the difference between a metaclass and a class? Can you demonstrate creating a metaclass?

Answer:
Classes create objects, and classes themselves are objects. Metaclasses create classes.

MyClass = MetaClass()  # Class created
my_object = MyClass()  # Object created from the class

# type() helps you create a metaclass:
MyClass = type('MyClass', (), {})

# type(name, base, dict)
# name => class name
# base => tuple of base classes
# dict => dictionary for attributes
MyMetaClass = type("MySimpleClass", (), {"val": 12})
obj = MyMetaClass()
print(obj.val)  # 12

Question 7

Question:
We have three different types of lists:

If we create a list with range(1, 10_000_000) and check the sum using sum(), we find that the regular list() computes the sum much faster than the others. Why is it so much faster, and how is it computed so efficiently?

import time
import numpy as np
from collections import UserList

regular_list = list(range(1, 10000000))  # Very fast
np_list = np.array(range(1, 10000000))  # Slow
user_list = UserList(range(1, 10000000))  # Slowest

start = time.perf_counter()
print("Sum of regular list:", sum(regular_list), f"Time taken: {time.perf_counter() - start:.6f} seconds")

start = time.perf_counter()
print("Sum of NumPy array:", sum(np_list), f"Time taken: {time.perf_counter() - start:.6f} seconds")

start = time.perf_counter()  # np.sum() is another matter
print("np.sum of NumPy array:", np.sum(np_list), f"Time taken: {time.perf_counter() - start:.6f} seconds")

start = time.perf_counter()
print("Sum of UserList:", sum(user_list), f"Time taken: {time.perf_counter() - start:.6f} seconds")

Answer:
This question is for you to figure out. Send your answer to me via email at [email protected].

To make the most of this post, study it and teach your friends (share it)!


This translation keeps your ideas clear and concise while adjusting phrasing for better readability in English. Let me know if any further changes are needed!