All Python Interview Questions

Why can’t a lambda function have statements in Python?

The body of a lambda must be one expression, because a lambda is only a short way to write a function that returns the value of that expression. Statements such as return, for, while, pass or an assignment are not expressions, so they are not allowed. Writing one gives a SyntaxError.

# This is NOT allowed
f = lambda x: return x * 2

You can still use an expression that makes a choice, like a conditional expression.

positive = lambda x: x if x > 0 else 0

print(positive(-5))
print(positive(8))

Output:

Tutorials Class - Output Window

0
8

If you need many lines, loops or several statements, write a normal function with def. Also read What is a lambda function?

What are public, protected and private attributes in Python?

Python has no strict access control like Java or C++. Instead it uses naming rules to show how an attribute should be used.

NameTypeMeaning
namePublicCan be used from anywhere
_nameProtectedBy convention only for the class and its child classes. Python does not stop you
__namePrivatePython renames it (name mangling), so it cannot be reached by its normal name from outside
class Account:
    def __init__(self):
        self.owner = "Robin"
        self._balance = 100
        self.__pin = 1234

a = Account()
print(a.owner)
print(a._balance)
print(a.__pin)

Output:

Tutorials Class - Output Window

Robin
100
AttributeError: 'Account' object has no attribute '__pin'

The private attribute is stored as _Account__pin behind the scenes, so it is hidden from casual use but not truly secret. See Python Access Modifiers for more.

What is a decorator in Python?

A decorator is a function that takes another function, adds some extra behaviour to it, and returns the new function. You apply it by writing @decorator_name above the function, and the original code stays unchanged.

def shout(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

@shout
def greet():
    return "hello"

print(greet())

Output:

Tutorials Class - Output Window

HELLO

Writing @shout above greet is the same as writing greet = shout(greet). Decorators are used for logging, timing, login checks and caching. Use functools.wraps inside real decorators to keep the original function name. Learn more in Python Decorators.

What are iterators in Python?

An iterator is an object that gives you the items of a collection one at a time. It follows the iterator protocol, which means it has two methods: __iter__() and __next__(). A list, tuple or string is an iterable, and you get an iterator from it with iter().

nums = [10, 20, 30]
it = iter(nums)
print(next(it))
print(next(it))
print(next(it))
print(next(it))

Output:

Tutorials Class - Output Window

10
20
30
Traceback (most recent call last):
File "main.py", line 6, in <module>
print(next(it))
StopIteration

When there are no items left, the iterator raises StopIteration. A for loop calls iter() and next() for you and stops quietly when this happens. Compare it with a generator, which is an easy way to create an iterator.

What is a generator in Python?

A generator is a function that uses the yield keyword to give values one at a time, instead of building the whole list in memory. Each time it yields, the function pauses and remembers where it stopped, and it continues from the same place on the next request.

def count_up(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up(3):
    print(num)

Output:

Tutorials Class - Output Window

1
2
3

There is also a short form called a generator expression, which looks like a list comprehension but uses round brackets.

squares = (x * x for x in range(5))
print(sum(squares))

Output:

Tutorials Class - Output Window

30

Generators are a good choice for big data, because values are made only when needed. A generator is also an iterator.

What is pickling and unpickling in Python?

Pickling is the process of converting a Python object into a stream of bytes so it can be saved in a file or sent over a network. Unpickling is the reverse: reading those bytes and rebuilding the original object. Both are done with the built-in pickle module.

import pickle

data = {"name": "Robin", "marks": [90, 85]}

with open("data.pkl", "wb") as f:
    pickle.dump(data, f)

with open("data.pkl", "rb") as f:
    loaded = pickle.load(f)

print(loaded)

Output:

Tutorials Class - Output Window

{'name': 'Robin', 'marks': [90, 85]}

Files must be opened in binary mode (wb and rb). Important: never unpickle data from a source you do not trust, because it can run harmful code. For data that other programs or languages also need, JSON is the safer choice.

How does Python do compile-time and run-time checking?

Python checks only a small part at compile time, which is when the source is turned into bytecode. This step finds syntax errors, like a missing colon or wrong indentation. Most other checks, such as names and types, happen at run time, when that line actually runs.

def show():
    print(undefined_name)

print("Program started")
show()

Output:

Tutorials Class - Output Window

Program started
Traceback (most recent call last):
File "main.py", line 5, in <module>
show()
File "main.py", line 2, in show
print(undefined_name)
NameError: name 'undefined_name' is not defined

The code compiled without any problem, even though undefined_name does not exist. The error came only when show() was called. A syntax error is different: the program does not start at all. This is the cost of a dynamically typed language, so good tests are important.

What is PYTHONPATH?

PYTHONPATH is an environment variable that tells Python about extra folders to search when you import a module. Python normally looks in the current folder and the standard library first, and then in the folders listed in PYTHONPATH.

Set it on macOS or Linux like this:

export PYTHONPATH=/home/robin/mylibs

On Windows (Command Prompt):

set PYTHONPATH=C:\mylibs

You can check the full list of search folders from Python itself.

import sys
print(sys.path)

Output (your computer will show different folders):

Tutorials Class - Output Window

[”, ‘/home/robin/mylibs’, ‘/usr/lib/python3.12’, …]

Your own folder appears in the list. This is useful when you keep shared code outside the project, but a virtual environment and proper packages are usually a cleaner way.

What is the difference between a list and a tuple in Python?

The main difference is that a list is mutable (you can change it) and a tuple is immutable (you cannot change it after creating it).

PointListTuple
Brackets[ ]( )
Can change itemsYesNo
Speed and memoryA little slower, uses more memoryA little faster, uses less memory
MethodsMany, like append() and remove()Only count() and index()
Use as dictionary keyNoYes, if it holds only immutable items
my_list = [1, 2, 3]
my_list[0] = 10
print(my_list)

my_tuple = (1, 2, 3)
my_tuple[0] = 10

Output:

Tutorials Class - Output Window

[10, 2, 3]
TypeError: 'tuple' object does not support item assignment

Use a list when the data will change, and a tuple when it should stay fixed. Read more about lists and tuples.

What are mutable and immutable types in Python?

A mutable object can be changed after it is created, and an immutable object cannot. When you “change” an immutable value, Python actually creates a new object.

MutableImmutable
list, dict, setint, float, bool, str, tuple, frozenset
numbers = [1, 2]
numbers.append(3)
print(numbers)

text = "hi"
print(text.upper())
print(text)

text[0] = "H"

Output:

Tutorials Class - Output Window

[1, 2, 3]
HI
hi
TypeError: 'str' object does not support item assignment

The list was changed in place, but the string stayed the same, and upper() gave a new string. This matters when you pass a list to a function, because the function can change the original list. See Python Data Types.

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

The == operator checks if two values are equal. The is operator checks if two names point to the same object in memory.

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)
print(a is b)
print(a is c)

Output:

Tutorials Class - Output Window

True
False
True

a and b have the same values but are two separate lists. c is just another name for the same list as a. Use == to compare values, and use is mainly for None, like if x is None:. See Python Operators.

What are *args and **kwargs in Python?

*args lets a function accept any number of positional arguments, which arrive as a tuple. **kwargs lets it accept any number of keyword arguments (name=value), which arrive as a dictionary.

def show(*args, **kwargs):
    print(args)
    print(kwargs)

show(1, 2, name="Robin", age=20)

Output:

Tutorials Class - Output Window

(1, 2)
{'name': 'Robin', 'age': 20}

The names args and kwargs are only a habit. The stars are what matter. They are useful when you do not know in advance how many values will be passed, for example in a function that adds any amount of numbers.

def total(*numbers):
    return sum(numbers)

print(total(1, 2, 3, 4))

Output:

Tutorials Class - Output Window

10

What is list comprehension in Python?

List comprehension is a short way to create a new list in a single line. The syntax is [expression for item in iterable if condition].

squares = [x * x for x in range(1, 6)]
print(squares)

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Output:

Tutorials Class - Output Window

[1, 4, 9, 16, 25]
[0, 2, 4, 6, 8]

The same work with a normal loop needs three or four lines:

squares = []
for x in range(1, 6):
    squares.append(x * x)

Comprehensions are shorter and usually a bit faster, but keep them simple. If the line becomes hard to read, use a normal loop. Python also has dictionary and set comprehensions. See Python for loop.

What is the difference between shallow copy and deep copy in Python?

A shallow copy creates a new outer object but the inner objects are still shared with the original. A deep copy creates a new outer object and also new copies of everything inside it.

import copy

original = [[1, 2], [3]]
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original[0].append(99)

print(shallow)
print(deep)

Output:

Tutorials Class - Output Window

[[1, 2, 99], [3]]
[[1, 2], [3]]

The shallow copy changed too, because its inner list is the same object as in the original. The deep copy stayed safe. Note that b = a does not copy at all, it only gives a second name to the same object. Use a deep copy when the data has lists or dictionaries inside other lists or dictionaries.

What is the GIL in Python?

The GIL (Global Interpreter Lock) is a lock inside CPython, the standard Python, that allows only one thread to run Python bytecode at a time. It keeps memory management simple and safe.

Because of the GIL, several threads doing heavy calculation do not run in parallel on different CPU cores. But it is released while a thread waits for files, network or database, so threads still work well for such waiting tasks.

Task typeBetter choice
Waiting for network, files, database (I/O bound)threading or asyncio
Heavy calculation (CPU bound)multiprocessing, where each process has its own interpreter and GIL

Python 3.13 also added an experimental build that can run without the GIL, but the normal build still has it. Other implementations such as Jython do not have a GIL.

How does exception handling work in Python?

Python handles errors with the try and except blocks. The risky code goes in try, and if an error happens, the matching except block runs instead of the program crashing.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero")
else:
    print(result)
finally:
    print("Done")

Output:

Tutorials Class - Output Window

Cannot divide by zero
Done

  • try: the code that may raise an error.
  • except: runs if that error happens. You can have more than one.
  • else: runs only if no error happened.
  • finally: always runs, good for cleanup like closing a file.

Catch the specific error you expect, not every error. You can also raise your own error with raise. See Python Errors.

What is the __init__() method in Python?

__init__() is a special method of a class that runs automatically when a new object is created. It is used to set the starting values of the object, so it works like a constructor in other languages.

class Car:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year

car = Car("Tata", 2024)
print(car.brand)
print(car.year)

Output:

Tutorials Class - Output Window

Tata
2024

The first parameter is always self. The other values come from the call Car("Tata", 2024). Technically the object is created by __new__() first and then __init__() sets it up, but you rarely write __new__() yourself. See Python Classes and Objects.

What is inheritance in Python?

Inheritance lets a new class (child) take the attributes and methods of an existing class (parent). It helps to reuse code, and the child can also change or add its own behaviour.

class Animal:
    def speak(self):
        print("Some sound")

class Dog(Animal):
    def speak(self):
        print("Bark")

class Cat(Animal):
    pass

Dog().speak()
Cat().speak()

Output:

Tutorials Class - Output Window

Bark
Some sound

Dog replaces (overrides) the speak() method, while Cat uses the one from Animal. Use super() to call a method of the parent class. Python supports single, multiple, multilevel, hierarchical and hybrid inheritance. See Python OOPS Introduction.

What is the difference between a module and a package in Python?

A module is a single Python file (.py) that has functions, classes or variables. A package is a folder that groups related modules together.

import math
print(math.sqrt(16))

from math import pi
print(pi)

Output:

Tutorials Class - Output Window

4.0
3.141592653589793

A package looks like this on disk:

shop/
    __init__.py
    cart.py
    payment.py
from shop import cart
from shop.payment import pay

The __init__.py file marks the folder as a regular package. Python can also use folders without it, but most projects still keep the file. Python comes with many built-in modules, and you can install more with pip.

What is a virtual environment in Python?

A virtual environment is an isolated folder that has its own Python packages for one project. It stops different projects from clashing, for example when one needs version 1 of a library and another needs version 2.

Create and use it with these commands. The first command is the same everywhere.

python -m venv venv

Activate it on macOS or Linux:

source venv/bin/activate

Activate it on Windows:

venv\Scripts\activate

Now install packages with pip, the Python package installer. They go only into this environment.

pip install requests
pip freeze > requirements.txt
pip install -r requirements.txt

pip freeze saves the list of installed packages, and the last command installs them again on another computer. Type deactivate to leave the environment.