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 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
PythonAre variables name case sensitive?
Variable names are case-sensitive.
a = 4
A = "Sally"
print(a) #4
print(A) #Sally
PythonDo 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
PythonAssign the same value to multiple variables at once
x = y = z = "Orange"
print(id(x)) #4359546952
print(id(y)) #4359546952
print(id(z)) #4359546952
z = "apple"
print(id(x)) #4359546952
print(id(y)) #4359546952
print(id(z)) #4359546248
Python- 1st Assignment: three variables
x
,y
, andz
refer to the same memory location where"Orange"
is stored. - 2nd Assignment: When you assign “apple” to z, Python creates a new string object “apple” in memory and assigns z to it
What’s happening
- Python reuses memory for immutable objects (like strings and small integers).
- When you assign the same immutable value to multiple variables, they share the same memory address.
- But if one variable is reassigned, it will get a new memory address while others remain unchanged.
What is String Interning in Python?
String interning is a performance optimization technique where Python stores only one copy of a string and reuses it, instead of creating multiple identical string objects in memory.
a = "hello"
b = "hello"
print(id(a)) # Example: 4359546952
print(id(b)) # Example: 4359546952 (Same memory address as `a`)
print(a is b) # True (Both refer to the same object)
PythonWhen Does Python Intern Strings?
Python automatically interns:
- Short strings (usually length ≤ 20 characters).
- Strings containing only letters, digits, or underscores (e.g., “Python123”, “var_name”).
- Strings that are assigned at compile-time (i.e., directly written in the code).
Does Python Intern Integers?
Yes! Python also interns integers, but only within a specific range to optimize memory usage.
What is the difference between Python Arrays and lists?
- Arrays in python can only contain elements of same data types i.e., data type of array should be homogeneous.
- It is a thin wrapper around C language arrays and consumes far less memory than lists.
- Lists in python can contain elements of different data types i.e., data type of lists can be heterogeneous. It has the disadvantage of consuming large memory.
When defining an array, you must specify a type code, which determines the type of elements it can store:
- ‘b’ – signed char (1 byte)
- ‘B’ – unsigned char (1 byte)
- ‘h’ – signed short (2 bytes)
- ‘H’ – unsigned short (2 bytes)
- ‘i’ – signed int (4 bytes)
- ‘I’ – unsigned int (4 bytes)
- ‘f’ – float (4 bytes)
- ‘d’ – double (8 bytes)
import array
# Create an array of integers
arr = array.array('i', [1, 2, 3, 4, 5])
# Append an element
arr.append(6)
# Remove an element
arr.remove(3)
# Access elements
print(arr[0]) # Output: 1
# Iterate through the array
for num in arr:
print(num)
PythonPrint statement
Basic Usage:
print("Hello,")
print("Backendmesh")
'''
Output
Hello,
Backendmesh
'''
PythonIn above example, print statement add newline(\n)
Custom end character
By default, print() adds a newline (\n) at the end. You can override it with end
print("Hello", end=" ")
print("World")
'''
Output
Hello World
'''
print("1", end=" ")
print("2")
print("3",end="->")
print("4")
print("5",end="--")
print("6")
'''
1 2
3->4
5--6
'''
PythonString formatting inside print()
print(f"My name is {name} and I am {age} years old.")
PythonMultiple values (separated by commas):
name = "Alice"
age = 30
print("Name:", name, "Age:", age)
'''
Name: Alice Age: 30
'''
PythonIn above example, values are separated by space
Custom separator
By default, print() separates items with a space. You can change this using the sep parameter:
print("2025", "04", "29", sep="-")
'''
Output
2025-04-29
''''
PythonCommon String inbuild functions
Case Conversion
s = "hello wOrld"
print(s.lower()) # 'hello world'
print(s.upper()) # 'HELLO WORLD'
print(s.capitalize()) # 'Hello world'
print(s.title()) # 'Hello World'
print(s.swapcase()) # 'HELLO WoRLD'
print(s.casefold()) # 'hello world' (more aggressive than lower)
Pythoncasefold() : Designed for aggressive and language-independent case conversion.
Search & Check
s = "hello world"
print(s.find("world")) # 6
print(s.rfind("l")) # 9 Returns the highest index of substring or -1
print(s.index("world")) # 6 Like find() but raises ValueError if not found
print(s.startswith("he")) # True
print(s.endswith("ld")) # True
print(s.count("l")) # 3
PythonTrimming
s = " hello "
print(s.strip()) # 'hello'
print(s.lstrip()) # 'hello ' Removes leading whitespace.
print(s.rstrip()) # ' hello' Removes trailing whitespace.
PythonReplace and Split
s = "apple,banana,grape"
print(s.replace("banana", "kiwi")) # 'apple,kiwi,grape'
print(s.split(",")) # ['apple', 'banana', 'grape']
PythonJoins elements with the string as separator
print("->".join(["a", "b", "c"])) # a->b->c
PythonExplain 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
PythonSorting
sorted() function
Returns a new sorted list from any iterable.
nums = [4, 2, 9, 1]
sorted_nums = sorted(nums) # [1, 2, 4, 9]
sorted_nums_desc = sorted(nums, reverse=True) # [9, 4, 2, 1]
Python.sort() method
Sorts a list in place and returns None.
nums = [4, 2, 9, 1]
nums.sort() # modifies nums to [1, 2, 4, 9]
nums.sort(reverse=True) # modifies nums to [9, 4, 2, 1]
PythonCustom Sorting
It’s important for interview.
Sorting with a custom key in Python allows you to define how the elements should be ordered by providing a function to the key parameter. This is especially useful when sorting complex data like dictionaries, tuples, or objects.
sorted(iterable, key=key, reverse=True/False)
Python- iterable Required. The sequence to sort, list, dictionary, tuple etc.
- key Optional. A Function to execute to decide the order. Default is None
- reverse Optional. A Boolean. False will sort ascending, True will sort descending. Default is False
Sorting by string length:
words = ['banana', 'pie', 'Washington', 'book']
sorted_words = sorted(words, key=len)
print(sorted_words) # ['pie', 'book', 'banana', 'Washington']
PythonSorting a List of Tuples
students = [('Bob', 20),('Alice', 25), ('Charlie', 23)]
sorted_by_name = sorted(students, key=lambda x: x[0])
sorted_by_age = sorted(students, key=lambda x: x[1])
print(sorted_by_name) # [('Alice', 25), ('Bob', 20), ('Charlie', 23)]
print(sorted_by_age) # [('Bob', 20), ('Charlie', 23), ('Alice', 25)]
PythonWorking
- Python calls lambda x: x[1] on each tuple.
- So it gets: 25, 20, 23
- It sorts those numbers: 20, 23, 25
- Then arranges the tuples accordingly.
Sorting a List of Dictionaries
products = [
{'name': 'Laptop', 'price': 1000},
{'name': 'Phone', 'price': 500},
{'name': 'Tablet', 'price': 700}
]
sorted_products = sorted(products, key=lambda x: x['price'])
# [{'name': 'Phone', 'price': 500}, {'name': 'Tablet', 'price': 700}, {'name': 'Laptop', 'price': 1000}]
PythonWorking
- lambda x: x[‘price’] extracts the price from each dictionary.
- So internally, Python compares 500, 1000, 700, and sorts accordingly
How lamda works for sorting?
A lambda helps in sorting by telling Python what to sort by — it acts as a custom rule for comparing elements. In place of lambda we can use our custom function as well
Sorting by custom function
students = [('Alice', 25), ('Bob', 20), ('Charlie', 23)]
def get_age(student):
return student[1]
sorted_students = sorted(students, key=get_age)
Pythonproducts = [{'name': 'Phone', 'price': 500}, {'name': 'Laptop', 'price': 1000}]
def get_price(product):
return product['price']
sorted_products = sorted(products, key=get_price)
PythonConcatenate 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
PythonWhat 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
PythonGlobal 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('Global ', message) #Global Hello
greet()
print('Global ', message) #Global Hello
Pythonmessage = 'Hello'
def greet():
message = "hi"
# declare local variable
print('Local ', message) #Local Hi
greet()
print('Global ', message) #Global Hello
PythonNonlocal 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()
PythonWithout 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- nonlocal keyword in Python is only applicable to enclosed(nested) functions
- It allows modifying variables from an enclosing (but not global) scope
Tricky Example
message = "King"
# 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()
print(message) #king
#OutPut
inner: mesh
outer: mesh
King
PythonWhat 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()
PythonIf 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
PythonThe 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
PythonAlso, 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
PythonRead 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
PythonCommon 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
PythonIn 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)
PythonParameters
- 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)
PythonWhat is Type?
Returns the type of an object.
print(type(10)) #<class 'int'>
print(type('10')) #<class 'str'>
PythonWhat 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
PythonWhat 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'
PythonWhat is __dict__?
- In Python, dict is a special attribute of objects that stores an object’s attributes (instance variables) in a dictionary format. It is commonly used to inspect an object’s properties dynamically.
- It’s a dunder(magic method)
class Movie:
def __init__(self, title, year):
self.title = title
self.year = year
movie = Movie("Inception", 2010)
print(movie.__dict__) # {'title': 'Inception', 'year': 2010}
PythonWork on both instance and class Read further
What is vars?
Returns the __dict__ attribute of an object, showing its namespace.
class Movie:
def __init__(self, title, year):
self.title = title
self.year = year
movie = Movie("Inception", 2010)
print(vars(movie)) # Same as movie.__dict__
print(vars(Movie)) # TypeError: vars() argument must have __dict__ attribute
Python- Works on instance
- Unlike dict, vars() does not work directly on a class
With Modules
vars() is useful for inspecting modules dynamically:
import math
print(vars(math)) # Prints all attributes of the math module
PythonWith No Arguments
If called without arguments, vars() returns the local symbol table (same as locals()):
x = 10
y = "hello"
print(vars()) # Shows all local variables
PythonAre they vars and __dict__ same?
vars() and __dict__ are similar but not exactly the same. Here’s how they compare:
- vars() is a function, while __dict__ is an attribute
- for instance: vars(obj) is a function that returns obj.__dict__ if the object has one.
- vars() Works on Modules, But __dict__ Doesn’t Always
- vars() Without Arguments Returns locals()
- __dict__ is a dunder, while vars is build in function
Takeaway:
- If you just want an object’s attributes, vars(obj) and obj.__dict__ are the same.
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__']
PythonThis 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']
PythonThis 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']
PythonOutput:
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).
Vars() vs dir()
dir() Shows More Than Just Instance Attributes
dir(obj) returns all attributes and methods, including:
- Instance attributes (__dict__)
- Methods (both user-defined and built-in)
- Inherited attributes from parent classes
- Special methods like __init__, __str__, etc.
dir() Works on Built-in Types (Unlike vars())
- vars() only works on objects with a __dict__ attribute (e.g., instances of user-defined classes).
- dir() works on all objects, including built-in types.
dir() Can Be Used Without Arguments
- dir() without arguments lists all variables in the current scope.
- vars() without arguments behaves like locals(), returning a dictionary of local variables.
locals()
locals() is a built-in function that returns a dictionary of local variables in the current scope.
def my_function():
a = 10
b = 20
print(locals()) # {'a': 10, 'b': 20}
my_function()
Pythonglobals()
locals() is a built-in function that returns a dictionary of local variables in the global scope.
x = 100
def test():
y = 200
print("locals:", locals()) # {'y': 200}
print("globals:", globals()) # Shows all global variables, including x
test()
PythonWhat 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'>
PythonRange
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)
Pythonfrozenset
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'>
PythonMultiline Strings
a = """Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua."""
print(a)
PythonStrings 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
PythonLooping Through a String
for x in "banana":
print(x)
PythonFinding a substring in a string
txt = "The best things in life are free!"
print("free" in txt) #True
PythonSlicing Strings
b = "Hello, World!"
print(b[2:5]) #llo
print(b[:5]) #Hello
print(b[2:]) #llo, World!
print(b[-5:-2]) #orl
PythonF-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
PythonBoolean 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
Pythonbool()
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
PythonMost 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
PythonThis 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
PythonKeyword 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
PythonWhat 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
PythonArguments 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
PythonWhat 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
Pythonarg1 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
PythonCombine 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
PythonWhat 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
PythonCase 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
a is None vs a == None
a is None
- Checks identity, not equality.
- Preferred way to check if a variable is None.
a == None
- Checks equality using __eq__ method.
- Can give unexpected results if a defines a custom __eq__ method.
- Not recommended for None comparison.
Why a is None is preferred?
In Python, None is a singleton — there is only one instance of None in a program.
Why not a==None?
Because == can be overridden by a class’s __eq()__ method, which may define “equality” in a different way that says a==None as false even if a =None
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
PythonNote
- 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"]
PythonConvenience 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
PythonWith this, users can do:
from mypackage import func1, func2
PythonCustom 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
PythonWhat 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
PythonWhat 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
PythonCase 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
PythonCase 3: When both parent and child have the init() method, but we want to 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
PythonGuess 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()
PythonOutput
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
A initialized
C initialized
B initialized
D initialized
Pythonread 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()
PythonKey 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()
Pythonis internally converted to:
Class.method(obj)
PythonExample 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.
PythonExample 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
PythonSuper 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
PythonWhat 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
PythonEmpty Class: To define a class without any methods or properties for now.
class MyClass:
pass
PythonEmpty Loops: To include a loop that does nothing (often temporarily during debugging).
for i in range(10):
pass
PythonConditionals 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")
PythonPass 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!
PythonIterator vs Iterable
Feature | Iterable | Iterator |
---|---|---|
Definition | Can be iterated over. | Used to fetch items from an iterable. |
Methods | Implements __iter__() . | Implements __iter__() and __next__() . |
Examples | Lists, tuples, sets, dictionaries. | The object returned by iter() function. |
State | Stateless. | Stateful (remembers its position). |
What is decorator?
In Python, decorators are a powerful and flexible way to modify or extend the behavior of functions or methods, without changing their actual code. A decorator is essentially a function that takes another function as an argument and returns a new function with enhanced functionality.
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” or Preferred Installer Program
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)
PythonOutput
Finally block executed.
Try block return
PythonThe finally block will execute before the return value is sent back to the caller.
Why should broad exception handling be avoided?
Broad exception handling (e.g., using except Exception: or except: without specifying the exception type) should generally be avoided because:
- Hides Specific Errors – It catches all exceptions, making it difficult to determine what went wrong. This can lead to unexpected behavior and harder debugging.
- Catches Unintended Exceptions – It may unintentionally catch exceptions that should be handled elsewhere, like KeyboardInterrupt or SystemExit, preventing proper program termination.
- Makes Debugging Harder – Without knowing the specific exception type, debugging becomes more challenging since the root cause is obscured.
- Can Lead to Silent Failures – If the exception is caught and ignored or logged insufficiently, the program may continue running in an incorrect state.
- Prevents Granular Handling – Different exceptions require different handling. Broad handling prevents applying specific recovery strategies for each exception type.
Better Alternatives
- Catch specific exceptions (e.g., except ValueError:).
- Use multiple except blocks for different exception types.
- Log the exception properly using logging.exception() before re-raising if necessary.
- If using a broad except, at least log the full error message and traceback.
Bad example
try:
num = int(input("Enter a number: "))
result = 10 / num
except Exception: # Bad practice
print("An error occurred.") # No useful error message
PythonGood Example
try:
num = int(input("Enter a number: "))
result = 10 / num
print(f"Result: {result}")
except ValueError:
print("Invalid input! Please enter a valid number.") # Handles invalid number input
except ZeroDivisionError:
print("Division by zero is not allowed.") # Handles division by zero
except Exception as e:
print(f"Unexpected error: {e}") # Logs the unexpected error
PythonBy 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
Pythonsix 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()
PythonMigration 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 to get script execution time?
Easiest Way
time python script.py
PythonOutput
real 0m0.187s
user 0m0.022s
sys 0m0.026s
Pythonif not a: vs if a is None:
if not a:
- Checks all falsy values, inclduing
- None
- False
- 0 (int)
- 0.0(float)
- “” (empty string)
- [], {}, set() (empty collections)
if a is None:
Strictly checks if a is None and nothing else.
uses
- Use if a is None: when you specifically want to check if a is None.
- Use if not a: when you want to check for any falsy value.
if string is None: # not executed
print("1")
if not string:
print("2") #2
if lst is None: # not executed
print("1")
if not lst:
print("2") #2
if a is None:
print("1") #1
if not a:
print("2") #2
Pythonjson.load vs json.loads
The difference between json.load() and json.loads() in Python is:
json.load(file_object)
- Used for reading JSON data from a file.
- Takes a file object as an argument.
import json
json_str = '{"name": "Alice", "age": 25}'
data = json.loads(json_str) # Converts JSON string to Python dictionary
print(data)
Pythonjson.loads(json_string)
- Used for parsing a JSON string
- Takes a string as an argument
import json
with open("data.json", "r") as file:
data = json.load(file) # Reads JSON from a file
print(data)
Pythonjson.dump vs json.dumps
json.dump(obj, file_object)
- Used to write a Python object to a file in JSON format.
- Takes a file object as an argument.
import json
data = {"name": "Alice", "age": 25}
with open("output.json", "w") as file:
json.dump(data, file, indent=4) # Writes JSON to a file
Pythonjson.dumps(obj)
- Used to convert a Python object into a JSON string.
- Takes a Python object (dict, list, etc.) and returns a JSON-formatted string.
import json
data = {"name": "Alice", "age": 25}
json_str = json.dumps(data, indent=4) # Converts to a JSON string
print(json_str)
PythonWhich is valid Json in Python?
print(json.loads("1")) # 1
print(json.loads("true")) # Output: True (bool)
print(json.loads("false")) # Output: False (bool)
print(json.loads("null")) # Output: None (NoneType)
print(json.loads("3.14")) # Output: 3.14 (float)
Pythonjson.loads() is designed to considered following as a valid JSON value.
- a number (like 1, 2.5, -3) can be a standalone valid JSON value.
- true, false etc
What are Higher-Order Functions?
Higher-order functions are functions that either:
- Take one or more functions as arguments
- Return a function as a result
They are a key feature of functional programming and help make code more modular and reusable.
Passing a Function as an Argument
def apply_function(func, value):
return func(value)
def square(x):
return x * x
print(apply_function(square, 5)) # Output: 25
PythonReturning a Function
def multiplier(n):
def multiply(x):
return x * n
return multiply
double = multiplier(2)
print(double(5)) # Output: 10
triple = multiplier(3)
print(triple(5)) # Output: 15
PythonName few built-in higher-order functions?
- map
- filter
- reduce
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
PythonDask: 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()
PythonUse 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
PythonUse 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.
What is PEP?
- Python Enhancement Proposals
- PEPs are design documents that provide information about new features, improvements, and best practices in Python. They serve as proposals for changes or additions to the language, libraries, or processes.
Some key types of PEPs:
- PEP 8 – Style guide for writing Python code
- PEP 20 – The Zen of Python (guiding principles)
- PEP 257 – Docstring conventions
- PEP 484 – Type hinting
What is literals?
Literals can be defined as a data which is given in a variable or constant.
What is docstring in Python?
A docstring (short for documentation string) is a special type of comment in Python that describes what a function, class, or module does. It is enclosed in triple quotes (“”” “”” or ”’ ”’) and is typically the first statement inside a function, class, or module.
def add_numbers(a: int, b: int) -> int:
"""
Adds two numbers and returns the result.
Parameters:
a (int): The first number.
b (int): The second number.
Returns:
int: The sum of the two numbers.
"""
return a + b
PythonWhat are protected and private attributes in Python?
In Python, protected and private attributes are used to control access to class members (variables and methods). However, Python does not enforce strict access control like some other languages (e.g., Java, C++). Instead, it relies on naming conventions to indicate the intended level of access.
Protected Attributes (single_underscore)
- A protected attribute is indicated by a single underscore ().
- It signals that the attribute should not be accessed directly outside the class but can still be accessed if necessary.
- It is only a convention, and Python does not enforce this restriction.
class Person:
def __init__(self, name, age):
self.name = name # Public attribute
self._age = age # Protected attribute
def _get_age(self): # Protected method
return self._age
p = Person("John", 30)
print(p.name) # John (Public, can be accessed)
print(p._age) # 30 (Protected, but still accessible)
print(p._get_age()) # 30 (Protected method, still accessible)
PythonThe _age attribute and _get_age() method should not be accessed directly, but Python does not prevent it.
Private Attributes (double_underscore)
- A private attribute is indicated by a double underscore ().
- Python applies name mangling, changing __attribute to _ClassName__attribute, making it harder (but not impossible) to access directly.
- This is used to avoid accidental modifications by subclasses.
class Person:
def __init__(self, name, age):
self.name = name # Public attribute
self.__age = age # Private attribute
def get_age(self): # Public method to access private attribute
return self.__age
p = Person("Alice", 25)
print(p.name) # Alice
# print(p.__age) # AttributeError: 'Person' object has no attribute '__age'
# Accessing private attribute using name mangling
print(p._Person__age) # 25 (Not recommended, but possible)
Python__age is not directly accessible, but it can still be accessed using _ClassName__attribute
What are modules and packages in Python?
Modules
A module is a single Python file (.py) that contains Python code such as functions, classes, and variables.
import math_operations
result = math_operations.add(5, 3)
print(result) # Output: 8
PythonPackages
A package is a collection of related Python modules organized in a directory. A package must contain an init.py file (empty or with initialization code) to be recognized as a package.
my_package/
│── __init__.py
│── math_operations.py
│── string_operations.py
PythonWhat is an Interpreted language?
- An Interpreted language executes its statements line by line.
- Languages such as Python, Javascript, R, PHP, and Ruby are prime examples of Interpreted languages.
- Programs written in an interpreted language runs directly from the source code, with no intermediary compilation step.
What are Python namespaces?
In Python, namespaces are used to manage the names of variables, functions, and objects to avoid conflicts. A namespace is essentially a container that maps names (keys) to objects (values).
Types of Python Namespaces
Built-in Namespace :
- Contains built-in functions and exceptions, like print(), len(), and Exception.
- Available everywhere in Python.
Global Namespace :
- Defined at the top level of a script or module.
- Includes variables and functions defined outside any function or class.
Local Namespace:
- Created inside a function and holds variables defined within that function.
- It only exists while the function runs.
Enclosing (Nonlocal) Namespace (Closures):
- Exists when a nested function is used.
- The inner function can access variables from the outer function’s scope (but not modify them unless nonlocal is used).
Namespace Scope
Python follows the LEGB rule to resolve variable names:
Local → Enclosing → Global → Built-in
Pythonx = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # "local" is printed first
inner()
print(x) # "enclosing" is printed next
outer()
print(x) # "global" is printed last
PythonWhy Are Namespaces Important?
- Prevents naming conflicts.
- Improves code organization and modularity.
- Helps Python efficiently manage memory.
How is memory managed in Python?
Memory management in Python is handled automatically using reference counting and garbage collection. Here’s a breakdown of how it works:
Reference Counting
- Every object in Python has an associated reference count, which keeps track of how many variables or data structures refer to it.
- When the reference count drops to zero, the memory occupied by the object is automatically deallocated.
import sys
a = [1, 2, 3]
print(sys.getrefcount(a)) # Reference count includes temporary references
b = a # Now two references exist
print(sys.getrefcount(a)) # Increased by 1
del a
print(sys.getrefcount(b)) # Decreased by 1, but still > 1 because of 'b'
OutPut
2
3
2
PythonExplanation:
First sys.getrefcount(a)
- When a is created, it has one reference.
- Passing it to sys.getrefcount(a) temporarily creates another reference.
- So, the count is 2.
After b = a, we now have two permanent references (a and b).
- Calling sys.getrefcount(a) again will include:
- a
- b
- The temporary reference from sys.getrefcount(a)
- So, the count becomes 3.
After del a, the reference from a is removed, leaving:
- b
- The temporary reference from sys.getrefcount(b)
- So, the count is back to 2.
However, if you run this in an interactive environment (like Jupyter Notebook or an IDE with debugging features), the reference count might be slightly different due to internal optimizations.
Garbage Collection (GC)
- Python has a built-in garbage collector to handle cyclic references (e.g., objects referring to each other).
- It uses Generational Garbage Collection, which divides objects into three generations:
- Gen 0 (youngest)
- Gen 1 (middle-aged)
- Gen 2 (oldest)
- The garbage collector runs periodically to clean up unreachable objects in these generations.
You can manually invoke it using:
import gc
gc.collect()
PythonMemory Allocation
- Python uses, PyMalloc (Python’s memory manager) to allocate memory efficiently.
- Small Object Allocator for objects smaller than 512 bytes.
- Heap Memory for larger objects.
Objects with Special Memory Handling
- Immutable types (int, str, tuple, etc.): Python caches small integers (-5 to 256) and short strings to reuse memory.
- Large Objects: Managed directly using the system’s memory allocator.
Avoiding Memory Leaks
- Circular references should be minimized (use weak references with weakref module).
- Large lists or dictionaries should be cleared explicitly if not needed.
- Use context managers (with statement) for handling files and resources properly.
Python support passed by value or pass by reference?
In Python, everything is passed by object reference, but the behavior depends on whether the object is mutable or immutable.
What is the difference between .py and .pyc files?
The difference between .py and .pyc files lies in their purpose and usage in Python:
.py Files (Python Source Code)
- These contain human-readable Python source code.
- They can be directly written, edited, and executed.
- When you run a .py file, Python compiles it into bytecode before executing it.
.pyc Files (Compiled Python Bytecode)
- These are compiled versions of .py files, containing bytecode.
- They are not human-readable.
- Python automatically generates them when a module is imported to speed up execution in future runs.
- They are typically stored in a pycache directory with a filename format like module.cpython-xx.pyc, where xx represents the Python version.
Key Differences
Feature | .py (Source Code) | .pyc (Compiled Bytecode) |
---|---|---|
Readability | Human-readable | Machine-readable (bytecode) |
Execution | Runs directly | Needs a Python interpreter to execute |
Generation | Written by the developer | Auto-generated by Python on import |
Speed | Needs to be compiled before execution | Executes faster as it skips compilation |
When Do You See .pyc Files?
When you import a module (import module_name), Python compiles it and stores the .pyc file in pycache. If the source code (.py) changes, Python will regenerate the .pyc file.
What are docstrings ?
Docstrings are multi-line string literals used to document Python modules, classes, functions, and methods. They typically follow the “””triple-double-quote””” format and provide information on what the code does, expected parameters, return values, and examples of usage.
class Car:
"""Represents a car with a brand and speed.
Attributes:
brand (str): The brand of the car.
speed (int): The speed of the car in km/h.
"""
def __init__(self, brand: str, speed: int):
"""Initializes a Car object.
Args:
brand (str): The brand of the car.
speed (int): The speed of the car in km/h.
"""
self.brand = brand
self.speed = speed
PythonWhat is the use of help()?
The help() function in Python is used to get documentation about modules, functions, classes, or objects. It provides details such as the docstring, method signatures, and descriptions, making it useful for understanding how to use a particular component.
help(len) #This shows documentation related to len.
help(str) #This shows documentation related to str.
PythonCustom class and function
class Person:
"""A class representing a person."""
def __init__(self, name, age):
"""Initialize the person with a name and age."""
self.name = name
self.age = age
def greet(self):
"""Greet the user."""
return f"Hi, I'm {self.name}!"
help(Person)
PythonOutput
Help on class Person in module __main__:
class Person(builtins.object)
| A class representing a person.
|
| Methods defined here:
|
| __init__(self, name, age)
| Initialize the person with a name and age.
|
| greet(self)
| Greet the user.
PythonTakeaway
- help() reads docstrings, so always include “””Triple quoted strings””” in your functions, classes, and modules.
- It’s a great way to document your code and make it easier to understand for yourself and others.
What is PYTHONPATH in Python?
PYTHONPATH is an environment variable in Python that specifies additional directories where Python should look for modules and packages when importing them.
What is pickling and unpickling?
Picking
Pickling is the process of converting a Python object into a byte stream, so it can be stored in a file or sent over a network. This allows you to save and reload Python objects later. The pickle module in Python is used for this purpose.
import pickle
data = {"name": "Alice", "age": 25, "city": "New York"}
# Pickling (Serializing)
with open("data.pkl", "wb") as file:
pickle.dump(data, file)
PythonUnpickling
Unpickling is the reverse process—converting a byte stream back into a Python object.
# Unpickling (Deserializing)
with open("data.pkl", "rb") as file:
loaded_data = pickle.load(file)
print(loaded_data)
PythonUse Cases
- Saving program states.
- Sending Python objects over a network.
- Storing complex data structures (e.g., dictionaries, lists) in a file.
Limitations
- The pickled data is Python-specific and not human-readable.
- It is not secure if loading untrusted data (can execute arbitrary code).
- Pickled files may not be compatible across different Python versions.
if security is a concern, JSON is a safer alternative to pickle because it only supports basic data types (strings, numbers, lists, dictionaries, etc.) and does not execute arbitrary code when deserialized.
Key Differences Between Pickle and JSON
Feature | Pickle | JSON |
---|---|---|
Format | Binary | Text (Readable) |
Security | Not Safe (Can execute code) | Safe |
Cross-Language Support | Python-specific | Works across different languages |
Supports Custom Objects | Yes | No (Only basic types) |
Speed | Faster | Slower |
Pickle vs JSON
Both pickle and JSON are used for serialization in Python, but they serve different purposes and have distinct trade-offs.
Feature | Pickle | JSON |
---|---|---|
Format | Binary | Text (Human-readable) |
Security | Not secure (Can execute arbitrary code) | Secure (No code execution) |
Cross-Language Support | Python-specific | Supported in many languages (JavaScript, Java, etc.) |
Speed | Faster (Optimized for Python) | Slower (Parses text) |
File Size | Smaller (Compact binary format) | Larger (Text-based) |
Supports Custom Objects | Yes (Can serialize any Python object) | No (Only basic data types: dict, list, int, str, etc.) |
Readability | Not human-readable | Human-readable |
Use Case | Python-only applications, quick object storage | Web APIs, cross-platform data sharing |
When to Use Pickle?
- You need to serialize complex Python objects (e.g., classes, tuples, sets).
- You are working only in Python (not sharing data with other languages).
- Performance is critical, and speed matters.
- Avoid if security is a concern—loading a malicious pickle file can execute arbitrary code.
When to Use JSON?
- You need human-readable data.
- You are working with web APIs (REST, JavaScript, etc.).
- Security and cross-language compatibility are required.
Takeaway
- Pickle: Best for Python-specific object storage (fast but unsafe).
- JSON: Best for data exchange (slightly slower but universal & safe).
For most cases, JSON is the better choice unless you specifically need to store Python objects without cross-language support.
What is the difference between xrange and range ?
In Python 2, there was a difference between xrange and range:
- range(): Returns a list containing all the numbers in the specified range. It consumes more memory for large ranges.
- xrange(): Returns an iterator that generates numbers lazily, meaning it doesn’t create a full list in memory, making it more memory efficient.
Python 3:
- xrange() was removed.
- range() behaves like xrange() from Python 2, meaning it returns a lazy sequence generator instead of a list.
Type of inheritance work in python?
- Single Inheritance : A child class inherits from a single parent class.
- Multiple Inheritance :A child class inherits from more than one parent class.
- Multilevel Inheritance :A child class inherits from a parent class, which itself inherits from another parent.
- Hierarchical Inheritance : Multiple child classes inherit from the same parent class.
- Hybrid Inheritance (Combination of multiple types): A mix of more than one type of inheritance.
What is Monkey patching?
Monkey patching is a technique used in dynamic languages like Python to modify or extend code at runtime. It allows you to change the behavior of modules, classes, or functions without modifying the original source code.
Monkey Patching a Function
Replacing an existing function with a new one at runtime.
def greet():
return "Hello!"
print(greet()) # Output: Hello!
# Monkey patching greet function
def new_greet():
return "Hi there!"
greet = new_greet # Apply monkey patch
print(greet()) # Output: Hi there!
PythonMonkey Patching a Class Method
Modifying a method of a class dynamically.
class Person:
def say_hello(self):
return "Hello!"
p = Person()
print(p.say_hello()) # Output: Hello!
# Monkey patching the method
def new_say_hello(self):
return "Hi there!"
Person.say_hello = new_say_hello # Apply monkey patch
print(p.say_hello()) # Output: Hi there!
PythonMonkey Patching an Instance Method
Changing behavior for one object only.
class Dog:
def bark(self):
return "Woof!"
dog1 = Dog()
dog2 = Dog()
print(dog1.bark()) # Output: Woof!
print(dog2.bark()) # Output: Woof!
# Monkey patching only for dog1
def quiet_bark(self):
return "woof..."
dog1.bark = quiet_bark.__get__(dog1) # Apply monkey patch
print(dog1.bark()) # Output: woof...
print(dog2.bark()) # Output: Woof! (remains unchanged)
PythonWith Statement in Python
The with statement in Python is used for resource management and exception handling, ensuring that resources like files or network connections are properly cleaned up after use. It is commonly used with context managers.
Syntax
with expression as variable:
# Code block
PythonCommon Use Cases
- File Handling: with open()
- Database Connections: with sqlite3.connect()
- Thread Locks: with threading.Lock()
- Temporary Files: with tempfile.TemporaryFile()
- Managing Network Connections: with socket.create_connection()
What is Context manager?
Context managers in Python are constructs that allow you to properly manage resources, ensuring that they are acquired and released correctly. They are primarily used with the with statement to handle resources like file handling, database connections, threading locks, etc.
Why Use Context Managers?
- Automatic resource management – Ensures resources like files, network connections, and database sessions are properly closed after use.
- Cleaner code – Reduces boilerplate code for try…finally blocks.
- Exception safety – Ensures resource cleanup even if an error occurs.
Using Built-in Context Managers
Many built-in Python objects provide context managers. For example: File Handling
with open("example.txt", "w") as file:
file.write("Hello, World!")
# File automatically closes here
PythonCreating Custom Context Managers
You can create your own context manager in two ways:
A. Using a Class with __enter__ and __exit__ Methods
class MyContext:
def __enter__(self):
print("Entering the context")
return self # The return value is assigned to 'as' variable in 'with'
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting the context")
# If exception occurs, it will still call this method
# Usage
with MyContext():
print("Inside the context block")
PythonOutput
Entering the context
Inside the context block
Exiting the context
Python__enter__()
__enter__() is the first method that gets called when you enter a with block using a context manager.
__exit__()
__exit__() is always called — whether:
- the with block completes normally, or
- an exception is raised inside the with block.
Using contextlib and @contextmanager Decorator
A more concise way is using the contextlib.contextmanager decorator:
from contextlib import contextmanager
@contextmanager
def my_context():
print("Entering the context")
yield # This is where the block inside 'with' runs
print("Exiting the context")
# Usage
with my_context():
print("Inside the context block")
PythonOutput
Entering the context
Inside the context block
Exiting the context
PythonWhen to Use Context Managers?
- File handling (open)
- Database connections
- Thread locks (threading.Lock())
- Network connections (e.g., requests.Session())
- Temporary resource allocation (e.g., temporary files)
Database Connection Example
import sqlite3
@contextmanager
def database_connection(db_name):
conn = sqlite3.connect(db_name)
cursor = conn.cursor()
try:
yield cursor # Provide the cursor object
conn.commit() # Commit if no error occurs
except Exception:
conn.rollback() # Rollback if an error occurs
raise
finally:
conn.close()
# Usage
with database_connection("test.db") as db:
db.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)")
db.execute("INSERT INTO users (name) VALUES ('Alice')")
PythonConclusion
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.
9 thoughts on “Essential 100+ Python Interview Questions”