Essential 50+ Python Interview Questions

In this guide, we’ll explore commonly asked Python interview questions, along with explanations to help you ace your next interview. Let’s dive into Python’s rich ecosystem and its real-world applications!

Table of Contents

python interview question

Python was created by whom and when?

Python was created by Guido van Rossum and released in 1991.

What is the latest Python version?

The latest version of Python is 3.13.0, which was released on October 7, 2024. Always check the latest version at the time of your python interviews.

How do we do the casting of the variables?

x = str(3)    # x will be '3'
y = int(3)    # y will be 3
z = float(3)  # z will be 3.0
Python

Are variables name case sensitive?

Variable names are case-sensitive.

a = 4
A = "Sally"

print(a) #4
print(A) #Sally
Python

Do we need to declare variables?

  • Variables do not need to be declared of any particular type.
  • A variable is created the moment you first assign a value to it, and you can even change the type after it has been set.

Assign values to multiple variables at once

x, y, z = "Orange", "Banana", "Cherry"

print(x) #Orange
print(y) #Banana
print(z) #Cherry
Python

Assign the same value to multiple variables at once

x = y = z = "Orange"
print(x) #Orange
print(y) #Orange
print(z) #Orange
Python

Explain the concept of unpacking a collection

If you have a collection of values in a list, tuple, etc. Python allows you to extract the values into variables. This is called unpacking.

fruits = ["apple", "banana", "cherry"]
x, y, z = fruits

print(x) #Orange
print(y) #Banana
print(z) #Cherry
Python

Concatenate of string and number?

  • For numbers, the + operator works as a mathematical addition operator
  • For string, the + operator works concatenation operator
  • The mix of string and numbers will give an error
x = "Python "
y = "is "
z = "awesome"
print(x + y + z) #Python is awesome


x = 5
y = 10
print(x + y)  #15


x = 5
y = "John"
print(x + y) #error 
Python

What are the different variable scopes?

In Python, we can declare variables in three different scopes: local scope, global, and nonlocal scope.

Local Variables

When we declare variables inside a function, they are only accessible within the function and have a local scope.

def greet():

    # local variable
    message = 'Hello'
    
    print('Local', message) # Hello

greet()


print(message) # this will give error
Python

Global Variables

In Python, a variable declared outside of the function or in a global scope is known as a global variable. This means that a global variable can be accessed inside or outside of the function.

# declare global variable
message = 'Hello'

def greet():
    # declare local variable
    print('Local', message) # Hello

greet()
print('Global', message) # Hello
Python

Nonlocal Variables

In Python, the nonlocal keyword is used within nested functions to indicate that a variable is not local to the inner function, but rather belongs to an enclosing function’s scope.

It allows you to modify that variable in the outer function from within the inner function.

With nonlocal

# outside function 
def outer():
    message = 'backend'

    # nested function  
    def inner():

        # declare nonlocal variable
        nonlocal message

        message = 'mesh'
        print("inner:", message) #inner: mesh

    inner()
    print("outer:", message) #outer: mesh

outer()
Python

Without nonlocal

# outside function 
def outer():
    message = 'backend'

    # nested function  
    def inner():
        message = 'mesh'
        print("inner:", message) #inner: mesh

    inner()
    print("outer:", message) #outer: backend

outer()
Python

What is the global keyword used for?

Variables that are created outside of a function are known as global variables.

x = "awesome"

def myfunc():
  print("Python is " + x)  #Python is awesome

myfunc()
Python

If you create a variable with the same name inside a function, this variable will be local, and can only be used inside the function. The global variable with the same name will remain as it was, global and with the original value.

x = "awesome"

def myfunc():
  x = "fantastic"
  print("Python is " + x)

myfunc()

print("Python is " + x)


#Python is fantastic
#Python is awesome
Python

The global Keyword

Variable x becomes global because of the global keyboard

def myfunc():
  global x
  x = "fantastic"

myfunc()

print("Python is " + x)

#Python is fantastic
Python

Also, use the global keyword if you want to change a global variable inside a function.

x = "awesome"

def myfunc():
  global x
  x = "fantastic"

myfunc()

print("Python is " + x)

#Python is fantastic
Python

Read about Nonlocal

What is id?

In Python, id is a built-in function used to retrieve the memory address of an object. Each object in Python has a unique identifier during its lifetime, and the id function provides this identifier. It is typically an integer that is unique to the object.

id(object)

x = 42
print(id(x))  # Unique identifier of the integer 42


a=b=c=4
print(id(a)) #9756320
print(id(b)) #9756320
print(id(c)) #9756320
a=2
b=3
print(id(a)) #9756256
print(id(b)) #9756288
print(id(c)) #9756320
Python

Common mistake

#Works
print(id(56)) # 9757984


#Will Not work
id = 42  # Shadows the built-in 'id'
print(id(56))  # TypeError: 'int' object is not callable
Python

In this case, Python cannot call the built-in id function because the local variable id has overshadowed it.

What are isinstance?

isinstance is a built-in Python function used to check whether an object belongs to a specific class or a tuple of classes. It is a reliable way to ensure that an object is of the desired type before performing certain operations on it.

isinstance(object, classinfo)
Python

Parameters

  • object: The object to check.
  • classinfo: A class, type, or a tuple of classes and types to check against.

Return Value

  • True if the object is an instance of the specified class or one of the classes in the tuple.
  • False otherwise.
x = 10
print(isinstance(x, int))  # True (x is an instance of int)
print(isinstance(x, str))  # False (x is not a string)
Python

What is Type?

Returns the type of an object.

print(type(10)) #<class 'int'>
print(type('10')) #<class 'str'>
Python

What is issubclass?

Checks if a class is a subclass of another class.

class Animal: pass
class Dog(Animal): pass

print(issubclass(Dog, Animal))  # True
print(issubclass(Animal, Dog))  # False
Python

What are hasattr, getattr, setattr and delattr?

  • hasattr: Checks if an object has a specific attribute.
  • getattr: Retrieves the value of an attribute from an object. Allows a default value if the attribute does not exist.
  • setattr: Sets an attribute on an object.
  • delattr: Deletes an attribute from an object.
class MyClass:
    def __init__(self):
        self.value = 42

obj = MyClass()
print(hasattr(obj, 'value'))  # True
print(hasattr(obj, 'name'))  # False

print(getattr(obj, 'value'))        # 42
print(getattr(obj, 'name', 0))  # 0 (default value)

setattr(obj, 'name','backendmesh')
print(obj.value)  # backendmesh

delattr(obj, 'value')
# print(obj.value)  # AttributeError: 'MyClass' object has no attribute 'value'
Python

What is vars?

Returns the __dict__ attribute of an object, showing its namespace.

class MyClass:
    def __init__(self):
        self.value = 42

obj = MyClass()
print(vars(obj))  # {'value': 42}
Python

What are built-in data types?

  • Text Type: str
  • Numeric Types: int, float, complex
  • Sequence Types: list, tuple, range
  • Mapping Type: dict
  • Set Types: set, frozenset
  • Boolean Type: bool
  • Binary Types: bytes, bytearray, memoryview
  • None Type: NoneType

Note

  • Int, or integer, is a whole number, positive or negative, without decimals, of unlimited length.
  • A frozenset in Python is an immutable version of a regular set

Example:

x = 1j
print(type(x)) #<class 'complex'>

x = range(6)
print(x)  #range(0, 6)
print(type(x)) #<class 'range'>
Python

Range

The range() function defaults to increment the sequence by 1, however, it is possible to specify the increment value by adding a third parameter: range(2, 30, 3):

for x in range(2, 30, 3):
  print(x) 
Python

frozenset

A frozenset in Python is an immutable version of a regular set. Once created, its contents cannot be modified, which makes it hashable and usable as a key in a dictionary or as an element of another set.

x = frozenset({"apple", "banana", "cherry"})
print(x) #frozenset({'cherry', 'banana', 'apple'})
print(type(x)) #<class 'frozenset'>
Python

Multiline Strings

a = """Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua."""
print(a)
Python

Strings are Arrays

Like many other popular programming languages, strings in Python are arrays of bytes representing Unicode characters. However, Python does not have a character data type, a single character is simply a string with a length of 1.

Square brackets can be used to access elements of the string.

a = "Hello, World!"
print(a[1]) #e
Python

Looping Through a String

for x in "banana":
  print(x)
Python

Finding a substring in a string

txt = "The best things in life are free!"
print("free" in txt) #True
Python

Slicing Strings

b = "Hello, World!"
print(b[2:5]) #llo
print(b[:5]) #Hello
print(b[2:]) #llo, World!
print(b[-5:-2]) #orl
Python

Read more

F-Strings

F-String was introduced in Python 3.6 and is now the preferred way of formatting strings. To specify a string as an f-string, simply put an f in front of the string literal, and add curly brackets {} as placeholders for variables and other operations.

age = 36
txt = f"My name is John, I am {age}"
print(txt) #My name is John, I am 36
Python

Boolean Values

In programming, you often need to know if an expression is True or False. You can evaluate any expression in Python, and get one of two answers, True or False.

print(10 > 9) #True
print(10 == 9) #False
print(10 < 9) #False
Python

bool()

The bool() function allows you to evaluate any value, and give you True or False in return,

print(bool("Hello")) #True
print(bool(15)) #True
Python

Most Values are True

  • Almost any value is evaluated as True if it has some sort of content.
  • Any string is True, except empty strings.
  • Any number is True, except 0.
  • Any list, tuple, set, and dictionary are True, except empty ones.

Some Values are False

Values that are evaluated as False,

  • empty values, such as (), [], {}, “”,
  • the number 0
  • The None
  • And of course, the value False evaluates to False.

Give shorthand if …else example

a = 2
b = 330

print("A") if a > b else print("B") #B
Python

This technique is known as Ternary Operators or Conditional Expressions.

Give examples of Positional and Keyword Arguments

Positional Argument: The value is passed to the function based solely on its position in the argument list.

def add(a, b):
    return a + b

result = add("John ", "Doe")  # 'John' goes to 'a', and 'Doe' goes to 'b' (based on position)
print(result)  # Output: John Doe
Python

Keyword Argument: The value is passed using the parameter name explicitly.

result = add(b="Doe", a="John ")  # Passing by parameter name
print(result)  # Output: John Doe
Python

What are Positional-Only Arguments?

Positional-only arguments in Python are function parameters that can only be passed by position, not by keyword. This ensures that certain arguments are specified in a specific order without using their parameter names.

In Python, you define positional-only arguments by placing a / in the function definition. Any arguments defined before the / are positional only.

def function_name(arg1, arg2, /, arg3, arg4):
    # Function body
Python

Arguments before / are positional-only:

  • These arguments cannot be passed using their names.
  • They must be passed in the correct order.

Arguments after / can be passed by position or keyword:

  • You can pass these arguments using their names or by their order.

The / itself is not a parameter but a separator indicating which arguments are positional-only.

def calculate(a, b, /, c, d):
    
    print(a,b,c,d)

calculate(1,2,3,4)       # 1 2 3 4     #Positional for all parameters
calculate(1,2,d=4,c=3)   # 1 2 3 4    


# Invalid calls:
#calculate(a=1, b=2, c=3, d=4)  # TypeError: 'a' and 'b' are positional-only
#calculate(1, b=2, c=3, d=4)    # TypeError: 'b' is positional-only
Python

What are Keyword-only arguments?

Keyword-only arguments are function parameters that can only be passed by their names, not by their position. This ensures clarity and prevents mistakes when calling the function, especially for optional or default parameters.

In Python, you define keyword-only arguments by placing a * in the function definition. Any parameters defined after the * must be passed as keywords.

def function_name(arg1, arg2, *, arg3, arg4):
    pass
Python

arg1 and arg2:

  • These are positional or keyword arguments.
  • They can be passed either by position or by name.

arg3 and arg4

  • These are keyword-only arguments because they come after the *.
  • They must be passed by their names explicitly.
# valid
function_name(1, 2, arg3=3, arg4=4)  # All arguments provided correctly

function_name(arg1=1, arg2=2, arg3=3, arg4=4)  # Passing everything by keyword


# Invalid
function_name(1, 2, 3, 4)  
# TypeError: function_name() takes 2 positional arguments but 4 were given

function_name(1, 2, arg3=3)  
# TypeError: missing a required argument: 'arg4'

function_name(arg1=1, arg2=2, 3, 4)  
# SyntaxError: positional argument follows keyword argument
Python

Combine Positional-Only and Keyword-Only

You can combine the two argument types in the same function. Any argument before the /, is positional-only, and any argument after the *, is keyword-only.

def my_function(a, b, /, *, c, d):
  print(a + b + c + d)

my_function(5, 6, c = 7, d = 8) #26
Python

What is the difference between is and == in Python?

  • is: Checks for object identity. It verifies whether two references point to the same object in memory
  • ==: Checks for equality. It determines whether the values of two objects are the same, regardless of whether they are the same object in memory.
a = [1, 2, 3]
b = a
print(b is a) True
print(b == a) True 


# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
b = a[:] 
print(b is a) #False
print(b == a) #True 

# Case 1 
a = 1000
b = 1000
print(id(a)) #23066280615280
print(id(b)) #23066280615280
print(a is b) #True


print(1000 is 10**3) #True ,#Case 1
print(1000 == 10**3) #True 

print("a" is "a") #True
print("aa" is "a" * 2) #True ,#Case 1

list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2)  # True: values are the same
print(list1 is list2)  # False: different objects in memory
Python

Case 1: It works because Python caches small integer objects, which is an implementation detail. For larger integers, this does not work. Read about Python’s interning mechanism

Note: This question is often asked in python interviews

What is intern mechanism?

In the String Intern mechanism in Python, when we create two strings with the same value – instead of allocating memory for both of them, only one string is committed to memory. The other one just points to that same memory location.

However, The string interning behaviour in Python can differ based on several factors, including the version of Python, the implementation of the interpreter, and the context in which the string is created. As a result, identical string values may not always be interned, and the behaviour can be difficult to predict in certain cases.

One reason why the string interning result can differ for the same string in Python is that Python interns only string literals and not string objects that are created at runtime. This means if a string is executed at compile time are same then the reference is the same. But if the execution is done at run time then the reference(id) is different. Refer

Forcing Distinct Objects – bypass interning

a = int(1000) + 0  # Ensures a new integer object is created
b = int(1000) + 0  # Ensures another new integer object is created

print(a == b)  # True: values are equal
print(a is b)  # False: guaranteed to be distinct objects
Python

Note

  • If you’re unsure whether Python will cache objects (like small integers or strings), you should avoid using is for value comparisons and instead use == to check for equality. This is the most reliable and portable approach.
  • Use ‘is’ only when you explicitly want to check object identity, i.e., whether two variables point to the same object in memory
  • Always use == to compare values unless you explicitly need to check for object identity.
  • When in doubt, avoid writing code that depends on Python’s interning or caching optimizations; such code may behave differently across environments or versions.

What is the purpose of Python’s __init__.py file in packages?

The __init__.py file plays an important role in Python packages. Here’s its purpose:

Indicates a Directory as a Package

  • In earlier versions of Python (before 3.3), the presence of an __init__.py file was required to indicate that a directory is a Python package. Without this file, Python would not treat the directory as a package, and you couldn’t import modules from it.
  • In modern Python versions (3.3 and later), __init__.py is optional, but its use is still common for package initialization or organizational purposes.

Package Initialization

  • When a package is imported (e.g., import mypackage), the code in the __init__.py file is executed. This makes it useful for initializing the package by:
    • Setting up package-level variables.
    • Importing specific modules or submodules.
    • Executing any setup code required when the package is imported.

Namespace Control

It allows control over what is exposed at the package level. You can define the __all__ list in __init__.py to specify which modules or objects are accessible when using from mypackage import *.

__all__ = ["module1", "module2"]
Python

Convenience Imports

  • You can use __init__.py to aggregate imports so that users can access submodules or functions directly from the package without needing to know its internal structure. For example:
# mypackage/__init__.py
from .module1 import func1
from .module2 import func2
Python

With this, users can do:

from mypackage import func1, func2
Python

Custom Behavior

  • It can contain any Python code that you want to execute upon importing the package, such as logging or dynamically modifying the package.

Why Empty __init__.py file

An empty __init__.py simply serves as a marker that tells Python to treat the directory as a package. In modern Python (version 3.3 and later), even this is optional, as Python can infer a directory is a package without the file. However, it’s still a common practice to include an empty init.py file for clarity and backward compatibility.

What is __init()__?

The __init()__ method is a special class method known as the initializer or constructor. It is automatically called when a class object is created. The purpose of this method is to initialize the class’s attributes or perform any required setup for the object.

class ClassName:
    def __init__(self, parameters):
        # Initialization code
Python

What is __del__()

__del__()__ is a special method used to perform cleanup actions when an object is about to be destroyed. This is called a destructor.

class ClassName:
    def __del__(self):
        # Cleanup code here
Python

What is dunder?

The double underscores (__) in Python, often referred to as “dunder” (short for “double underscore”), are used to indicate special methods or magic methods. These methods have specific roles and behaviours defined by Python, and they are not meant to be called directly by the user. Instead, they are invoked automatically in certain situations.

Why Double Underscores?

  • Namespace Avoidance: Double underscores help differentiate special methods from user-defined methods, reducing the risk of accidental name collisions.
  • For example, your custom method init won’t conflict with Python’s __init()__.
  • Readability: Using __ visually sets these methods apart, making code easier to understand.

List Special Methods

Here’s a list of some commonly used double underscore methods:

  • __init__: Constructor for initializing new objects.
  • __del__: Destructor for cleanup when an object is deleted.
  • __str__: String representation of an object (used by str() or print).
  • __repr__: Official string representation for debugging (used by repr()).
  • __len__: Returns the length of an object (used by len()).
  • __add__: Defines the behaviour of the + operator.
  • __getitem__: Enables indexing or slicing (obj[index]).
  • __setitem__: Handles item assignment (obj[index] = value).

Role of __init()__ in inheritance

Case 1: When parent has init() method, but child does’nt have

class Parent:
    def __init__(self):
        print("Parent initialized")
              
class Child(Parent):
    pass      
        
obj = Child() # Parent initialized
Python

Case 2: When both parent and child have the init() method.

If a child class defines its __init__() method, it overrides the parent’s __init__()

class Parent:
    def __init__(self):
        print("Parent initialized")
              
class Child(Parent):
    def __init__(self):
        print("Child initialized")      
        
obj = Child() # Child initialized
Python

Case 3: When both parent and child have the init() method, but we can use the parent’s init() method

you need to explicitly call its __init__() method within the child class’s __init__() method using super

class Parent:
    def __init__(self):
        print("Parent initialized")
              
class Child(Parent):
    def __init__(self):
        super().__init__()
        print("Child initialized")      
        
obj = Child()

#Parent initialized
#Child initialized
Python

Guess the output

class A:
    def __init__(self):
        print("A initialized")

class B(A):
    def __init__(self):
        super().__init__()
        print("B initialized")

class C(A):
    def __init__(self):
        super().__init__()
        print("C initialized")

class D(B, C):
    def __init__(self):
        super().__init__()
        print("D initialized")


print(D.mro())  
obj = D()
Python
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

A initialized
C initialized
B initialized
D initialized
Python

read about MRO

Self Parameter

The self parameter in Python is a reference to the instance of the class itself. It is used within a class to access the instance’s attributes and methods.

self.attribute_name
self.method_name()
Python

Key Points About Self

  • The first parameter of any instance method in a class must be self.
  • It acts as a handle for the calling object, allowing access to its properties and other methods.
  • Self is not a reserved keyword in Python; it’s just a naming convention. You could name it anything else, but using self is strongly recommended for readability and consistency.

Working

obj.method()
Python

is internally converted to:

Class.method(obj)
Python

Example 1

class Person:
    def __init__(self, name, age):
        self.name = name  # Instance variable
        self.age = age    # Instance variable

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

# Creating an instance of Person
person1 = Person("Alice", 30)

# Calling an instance method
print(person1.greet())  # Output: Hello, my name is Alice and I am 30 years old.
Python

Example 2: Use backendmesh as reference instead of self

class Animal:
  def __init(backendmesh,name)__:
    backendmesh.name = name
    
  def speak(backendmesh):
    print(backendmesh.name)

cat = Animal("cat")
cat.speak() # cat
Python

Super function

In Python, super() is a built-in function used to call methods from a parent (or superclass) class. It is typically used in object-oriented programming (OOP) to invoke methods that are inherited from a parent class, especially when overriding them in a subclass.

class Animal:
    def speak(self):
        print("Animal speaks")

class Dog(Animal):
    def speak(self):
        super().speak()  # Calls speak method of Animal class
        print("Dog barks")

dog = Dog()
dog.speak()

#output
Animal speaks
Dog barks
Python

What is a pass statement in Python?

The pass statement is a placeholder that does nothing. It is used when a statement is syntactically required, but no action is necessary or the code is intentionally left blank.

Common Uses of Pass

Placeholder for Future Code: It can be used when writing a function, class, or loop where the implementation will be added later.

def my_function():
    pass  # Implementation will be added later
Python

Empty Class: To define a class without any methods or properties for now.

class MyClass:
    pass
Python

Empty Loops: To include a loop that does nothing (often temporarily during debugging).

for i in range(10):
    pass
Python

Conditionals Without Actions: When you want to skip specific conditions.

x = 5
if x > 10:
    pass  # Do nothing if x is greater than 10
else:
    print("x is less than or equal to 10")
Python

Pass in an Abstract Class

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def make_sound(self):
        pass  # No implementation here; subclasses must define this method

class Dog(Animal):
    def make_sound(self):
        print("Woof!")

class Cat(Animal):
    def make_sound(self):
        print("Meow!")

# Using the classes
dog = Dog()
dog.make_sound()  # Outputs: Woof!

cat = Cat()
cat.make_sound()  # Outputs: Meow!
Python

Iterator vs Iterable

FeatureIterableIterator
DefinitionCan be iterated over.Used to fetch items from an iterable.
MethodsImplements __iter__().Implements __iter__() and __next__().
ExamplesLists, tuples, sets, dictionaries.The object returned by iter() function.
StateStateless.Stateful (remembers its position).

Read More

What is dir() frunction

The dir() function in Python is used to return a list of the attributes and methods available in a given object. If called without arguments, it returns the list of names in the current local scope.

Without Arguments:

print(dir())

#['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
Python

This will list the names currently defined in the local scope.

With an Object:

print(dir(str))

#['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
Python

This will list all the attributes and methods of the str class.

Custom class

# Define a custom class
class MyClass:
    class_var = "Hello, World!"
    
    def __init__(self, value):
        self.instance_var = value
    
    def say_hello(self):
        return "Hello from MyClass!"
    
    def display_value(self):
        return f"The value is: {self.instance_var}"

# Create an instance of the class
my_object = MyClass(42)

# Use dir() to inspect the class
print("Attributes and methods in MyClass:")
print(dir(MyClass))

# Use dir() to inspect the object instance
print("\nAttributes and methods in my_object:")
print(dir(my_object))



#output 
Attributes and methods in MyClass:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'class_var', 'display_value', 'say_hello']

Attributes and methods in my_object:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'class_var', 'display_value', 'instance_var', 'say_hello']
Python

Output:
This will display all the attributes and methods available for MyClass and the my_object instance. It will include:

  • Your defined methods (say_hello, display_value)
  • Built-in methods and attributes such as __init__, __class__, and __repr__
  • Variables such as class_var and instance_var (accessible depending on the context).

What is PIP?

PIP is a package manager for Python packages.

  • Pip uses the Python Package Index (PyPI) as the default repository for fetching packages, but it can also install packages from other sources. 
  • Pip stands for “pip Install Packages”

Purpose of else in Try-Except

  • The try block lets you test a block of code for errors.
  • The except block lets you handle the error.
  • The else block lets you execute code when there is no error.
  • The finally block lets you execute code, regardless of the result of the try- and except blocks.

Key Differences Between else and finally

  • else: Runs only if the try block completes successfully without exceptions.
  • finally: Always runs, whether an exception occurs or not, and even if the try or except block contains a return, break, or continue.

Tricky question: Guess the Output

def test_function():
    try:
        return "Try block return"
    finally:
        print("Finally block executed.")

result = test_function()
print(result)
Python

Output

Finally block executed.
Try block return
Python

The finally block will execute before the return value is sent back to the caller.

Why should broad exception handling (using except Exception) be avoided?

Broad exception handling should be avoided because it can mask bugs, suppress critical errors, and make debugging difficult. When you catch all exceptions, you risk hiding issues like SyntaxError or NameError, which are important to address. It also prevents proper handling of specific problems, such as network errors or file access issues, and can lead to unintended side effects, like incorrect results or data corruption. Instead, it’s better to catch specific exceptions (e.g., ValueError, KeyError) that are expected in the context of the code, and for unexpected exceptions, log them and re-raise them for proper tracking. This approach ensures that your code is more maintainable, predictable, and easier to debug while still gracefully handling issues when they arise.

Example of Broad Exception Handling (to avoid):

try:
    # Some code that could raise different types of exceptions
    result = 10 / 0  # Division by zero will raise ZeroDivisionError
    data = {"key": "value"}
    print(data["non_existent_key"])  # This will raise KeyError
except Exception as e:  # Catching all exceptions
    print(f"An error occurred: {e}")
Python

Problems with this approach:

  • Masking specific errors: It catches all exceptions, including ZeroDivisionError and KeyError, but it doesn’t tell us where or why those specific errors occurred.
  • Debugging difficulty: You lose important context about what went wrong, making debugging much harder.
  • Silencing critical issues: Unexpected errors could be hidden, preventing you from identifying the root cause.

Better Approach: Catching Specific Exceptions

try:
    # Some code that could raise different types of exceptions
    result = 10 / 0  # Division by zero will raise ZeroDivisionError
except ZeroDivisionError as e:
    print(f"Cannot divide by zero: {e}")

try:
    data = {"key": "value"}
    print(data["non_existent_key"])  # This will raise KeyError
except KeyError as e:
    print(f"Missing key in dictionary: {e}")
Python

Benefits of this approach:

  • Specific error handling: Each exception (ZeroDivisionError and KeyError) is caught separately, allowing you to handle them appropriately.
  • Clear debugging: The error messages provide more context about where the problem occurred and why.
  • No masking: Errors are explicitly addressed without suppressing them, making it easier to identify and fix issues.

By catching specific exceptions, you ensure that the program reacts to errors in a predictable and controlled way, which makes your code easier to maintain and debug.

How would you ensure a Python script is compatible across Python 2 and Python 3?

Ensuring compatibility between Python 2 and Python 3 involves careful design and use of libraries that work across both versions. Here’s a comprehensive guide:

Compatibility Layer

A compatibility layer in the context of Python is a library or framework that helps developers write code that works seamlessly across both Python 2 and Python 3 without requiring separate code bases. These layers provide abstractions and helper functions to deal with differences in syntax, built-in functions, modules, and behaviour between the two Python versions.

Common Compatibility Layers

future Library

The future package allows you to write code in a Python 3 style that also works in Python 2.

from __future__ import print_function, division, absolute_import, unicode_literals
Python

six Library

The six library is lightweight and provides utilities for writing code compatible with Python 2 and Python 3.

from six.moves import queue
q = queue.Queue()
Python

Migration Approach

If possible, migrate code to Python 3 using tools like 2to3 or futurize. This way, you maintain Python 3 compatibility while supporting Python 2.

What is the purpose of ABC?

ABC stands for Abstract Base Class in Python. It provides a way to define abstract classes, which serve as a blueprint for other classes. Using the abc module, ABCs ensure that derived classes implement specific methods, making it a useful tool for enforcing a consistent interface.

Purpose of ABC:

  • Enforce Method Implementation: Abstract base classes allow you to define methods that must be implemented in any subclass. If a subclass fails to implement the abstract methods, Python raises a TypeError.
  • Encourage Code Reusability: By defining common interfaces and behaviour in an abstract class, derived classes can reuse those implementations, reducing redundancy.
  • Provide a Template: ABCs are used to outline the methods and properties a class should have, serving as a guideline for developers.
  • Enable Polymorphism: ABCs ensure that different objects adhering to the same interface can be treated interchangeably in the code.
  • Support for Type Checking: Using ABCs, you can check if an object is an instance of a certain abstract class or implements a specific interface.
from abc import ABC, abstractmethod

# Define an Abstract Base Class
class Animal(ABC):

    @abstractmethod
    def sound(self):
        pass

    @abstractmethod
    def habitat(self):
        pass

# Subclass implementing the abstract methods
class Dog(Animal):
    def sound(self):
        return "Bark"

    def habitat(self):
        return "Domestic"

class Fish(Animal):
    def sound(self):
        return "Blub"

    def habitat(self):
        return "Water"

# Instantiate and use the subclasses
dog = Dog()
fish = Fish()

print(dog.sound())   # Output: Bark
print(fish.habitat()) # Output: Water
Python

  • The Animal class is an abstract base class.
  • Dog and Fish are concrete implementations of Animal, ensuring they define sound and habitat.

What is a metaclass in Python?

A metaclass in Python is a “class of a class” that defines how a class behaves. Just like a class defines the behaviour and properties of objects, a metaclass defines the behaviour and properties of classes. Essentially, metaclasses allow you to control the creation, modification, and behaviour of classes themselves.

By default, Python uses the built-in metaclass type to create classes, but you can define your metaclass by inheriting from type.

When to Use Metaclasses

Metaclasses are typically used when you need to:

  • Enforce coding standards or ensure specific patterns in class definitions.
  • Automatically modify or add methods/attributes to classes when they’re defined.
  • Implement frameworks or libraries where certain rules or behaviours need to be standardized across many classes.

How can you debug a Python script?

Debugging a Python script effectively involves various tools and techniques. Here are three common methods:

  • Using Built-in Debugging Tools: pdb (Python Debugger)
  • Print Statements
  • IDE Debugging Features
    • Integrated Development Environments (IDEs) like PyCharm, VS Code, or Jupyter Notebooks have built-in debugging tools with graphical interfaces.
    • Features like breakpoints, variable inspection, and step-by-step execution make it easier to locate and resolve issues

You are given a large dataset that doesn’t fit into memory. How would you process it?

Use Chunking with Libraries:

Many libraries support processing large datasets in chunks.

Pandas

import pandas as pd

# Read the dataset in chunks
chunk_size = 100000  # Number of rows per chunk
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
    # Process each chunk
    print(chunk.head())  # Example: Print the first few rows of each chunk
Python

Dask: Dask provides a scalable way to process large datasets.

import dask.dataframe as dd

# Load a large CSV file as a Dask DataFrame
df = dd.read_csv('large_file.csv')

# Perform operations (these are lazy and will compute only when needed)
result = df.groupby('column_name').mean()

# Compute the result
result.compute()
Python

Use Generators for Lazy Loading

Generators allow you to load and process data lazily, row by row or in small chunks.

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line  # Yield one line at a time

# Process the file
for line in read_large_file('large_file.txt'):
    print(line)  # Example: Print each line
Python

Use Database Queries

Instead of loading the entire dataset into memory, query only the required data from a database.

Utilize File Formats Designed for Large Data

File formats like Parquet or HDF5 are optimized for large datasets and allow partial reads.

Parallel or Distributed Processing

Divide the dataset and process parts in parallel using tools like multiprocessing, Ray, or Dask.

Stream Data with Libraries

For specific formats like JSON, use libraries that support streaming.

Conclusion

Learning the basics of Python is the first step toward unlocking its vast potential. From understanding variables and control flow to working with data structures and functions, these fundamental concepts are essential for solving real-world problems with Python. With practice, you can confidently advance to more complex topics like object-oriented programming, file handling, and working with external libraries. Embrace the learning process, experiment with code, and enjoy the simplicity and power that Python offers.

Resources

3 thoughts on “Essential 50+ Python Interview Questions”

Leave a Comment