Master Python interviews with our comprehensive guide to the top 50 Python interview questions and answers. Covers basics, OOP, data structures, libraries, and coding challenges for freshers and experienced developers.
Python is one of the most in-demand programming languages today. Whether you're interviewing for a software developer, data scientist, automation engineer, or backend developer role, you'll likely face Python questions.
This comprehensive guide covers 50 essential Python interview questions organized by difficulty and topic, with detailed answers and code examples.
| Category | Questions | Focus |
|---|---|---|
| Basic Python | 1-15 | Fundamentals, syntax, data types |
| Intermediate | 16-30 | OOP, functions, error handling |
| Advanced | 31-40 | Decorators, generators, memory |
| Data Structures & Algorithms | 41-45 | Common coding problems |
| Libraries & Frameworks | 46-50 | NumPy, Pandas, Django |
Answer: Python is a high-level, interpreted, dynamically-typed programming language created by Guido van Rossum in 1991.
Key Features:
Answer:
| Feature | Python 2 | Python 3 |
|---|---|---|
print "Hello" | print("Hello") | |
| Division | 5/2 = 2 (integer) | 5/2 = 2.5 (float) |
| Unicode | Strings are ASCII by default | Strings are Unicode by default |
| Input | raw_input() | input() |
| Range | xrange() for iteration | Only range() exists |
| Support | No longer supported | Active development |
Note: Python 2 is deprecated. Always use Python 3 for new projects.
Answer:
| Type | Examples | Mutable? |
|---|---|---|
| Numeric | int, float, complex | No |
| Sequence | list, tuple, range | list: Yes, others: No |
| Text | str | No |
| Set | set, frozenset | set: Yes, frozenset: No |
| Mapping | dict | Yes |
| Boolean | bool | No |
| Binary | bytes, bytearray, memoryview | bytearray: Yes, others: No |
| None | NoneType | No |
Answer:
| Feature | List | Tuple |
|---|---|---|
| Syntax | [1, 2, 3] | (1, 2, 3) |
| Mutability | Mutable (can change) | Immutable (cannot change) |
| Performance | Slower | Faster |
| Memory | More memory | Less memory |
| Use case | When data changes | When data is constant |
| Methods | Many (append, remove, etc.) | Few (count, index) |
Example:
# List - can modify
my_list = [1, 2, 3]
my_list[0] = 10 # Works
# Tuple - cannot modify
my_tuple = (1, 2, 3)
my_tuple[0] = 10 # TypeError!
Answer: They allow functions to accept variable numbers of arguments.
*args: Accepts any number of positional arguments as a tuple.
def sum_all(*args):
return sum(args)
print(sum_all(1, 2, 3, 4)) # Output: 10
****kwargs**: Accepts any number of keyword arguments as a dictionary.
def print_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="John", age=25, city="Delhi")
# Output:
# name: John
# age: 25
# city: Delhi
Combined:
def combined_example(required, *args, **kwargs):
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
combined_example("hello", 1, 2, 3, name="John", age=25)
== and is?Answer:
== compares values (equality)is compares identity (same object in memory)a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # True (same values)
print(a is b) # False (different objects)
print(a is c) # True (same object)
# Check with id()
print(id(a), id(b), id(c)) # a and c have same id
When to use each:
== for value comparison (most common)is to check if two variables point to the same objectis for None checks: if x is None:Answer: Decorators are functions that modify the behavior of other functions or classes without changing their source code.
Simple Example:
def my_decorator(func):
def wrapper():
print("Before function")
func()
print("After function")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Output:
# Before function
# Hello!
# After function
Practical Example - Timing:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.2f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(2)
return "Done"
slow_function() # slow_function took 2.00s
Answer: List comprehensions provide a concise way to create lists.
Syntax: [expression for item in iterable if condition]
Examples:
# Basic
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# With condition
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# With expression
words = ["hello", "world"]
upper_words = [word.upper() for word in words]
# ['HELLO', 'WORLD']
# Nested
matrix = [[1, 2], [3, 4], [5, 6]]
flattened = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6]
Dictionary and Set Comprehensions:
# Dictionary comprehension
squares_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# Set comprehension
unique_lengths = {len(word) for word in ["hello", "world", "hi"]}
# {2, 5}
Answer:
Memory Management Components:
Private Heap: All objects and data structures are stored in a private heap managed by Python's memory manager.
Reference Counting: Python tracks how many references point to each object. When count reaches 0, memory is freed.
import sys
a = [1, 2, 3]
print(sys.getrefcount(a)) # Shows reference count (includes func argument)
import gc
# Force garbage collection
gc.collect()
# Get garbage collection stats
print(gc.get_stats())
Answer: Generators are functions that yield values one at a time instead of returning all at once. They're memory-efficient for large datasets.
Creating a Generator:
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
# Using the generator
for num in count_up_to(5):
print(num)
# Output: 1, 2, 3, 4, 5
# Generator object
gen = count_up_to(3)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
print(next(gen)) # StopIteration error
Generator Expression:
# Like list comprehension but with parentheses
squares_gen = (x**2 for x in range(1000000))
# Memory efficient - doesn't store all values
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
Answer: The GIL is a mutex in CPython that allows only one thread to execute Python bytecode at a time.
Implications:
Workarounds:
# For CPU-bound: Use multiprocessing
from multiprocessing import Pool
def cpu_heavy(n):
return sum(range(n))
with Pool(4) as p:
results = p.map(cpu_heavy, [1000000, 2000000, 3000000])
# For I/O-bound: Threading still works well
import threading
import requests
def fetch_url(url):
return requests.get(url)
# Threading is fine for I/O
threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls]
Answer: Lambda functions are small anonymous functions defined with the lambda keyword.
Syntax: lambda arguments: expression
Examples:
# Basic lambda
square = lambda x: x ** 2
print(square(5)) # 25
# Multiple arguments
add = lambda x, y: x + y
print(add(3, 4)) # 7
# With built-in functions
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
# [1, 4, 9, 16, 25]
# Sorting with lambda
students = [("John", 25), ("Jane", 22), ("Bob", 27)]
sorted_by_age = sorted(students, key=lambda x: x[1])
# [('Jane', 22), ('John', 25), ('Bob', 27)]
# Filter with lambda
evens = list(filter(lambda x: x % 2 == 0, numbers))
# [2, 4]
Answer:
Shallow Copy: Creates a new object but references the same nested objects.
Deep Copy: Creates a new object and recursively copies all nested objects.
import copy
original = [[1, 2, 3], [4, 5, 6]]
# Shallow copy
shallow = copy.copy(original)
shallow[0][0] = 'X'
print(original) # [['X', 2, 3], [4, 5, 6]] - Changed!
# Reset
original = [[1, 2, 3], [4, 5, 6]]
# Deep copy
deep = copy.deepcopy(original)
deep[0][0] = 'X'
print(original) # [[1, 2, 3], [4, 5, 6]] - Unchanged
Visual:
Shallow Copy:
original → [list1, list2]
shallow → [ ↑ , ↑ ] (points to same lists)
Deep Copy:
original → [list1, list2]
deep → [copy1, copy2] (completely independent)
Answer:
Namespace: A container that maps names to objects (like a dictionary).
Types of Namespaces:
print, len)LEGB Rule (Scope Resolution Order): Local → Enclosing → Global → Built-in
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # Prints "local"
inner()
print(x) # Prints "enclosing"
outer()
print(x) # Prints "global"
Modifying Outer Scopes:
counter = 0
def increment():
global counter # Required to modify global
counter += 1
def outer():
count = 0
def inner():
nonlocal count # Required to modify enclosing
count += 1
inner()
return count
Answer:
Basic Try-Except:
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
Multiple Exceptions:
try:
value = int("abc")
except ValueError:
print("Invalid integer")
except TypeError:
print("Type error")
except Exception as e: # Catch-all
print(f"Unexpected error: {e}")
Full Structure:
try:
file = open("file.txt")
content = file.read()
except FileNotFoundError:
print("File not found")
except PermissionError:
print("Permission denied")
else:
print("File read successfully") # Runs if no exception
finally:
file.close() # Always runs
Raising Exceptions:
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
return age
# Custom exception
class CustomError(Exception):
pass
raise CustomError("Something went wrong")
Answer:
Four Pillars of OOP:
# 1. ENCAPSULATION - Bundling data and methods
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
# 2. ABSTRACTION - Hiding complexity
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass # Must be implemented by subclasses
# 3. INHERITANCE - Deriving from parent class
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
# 4. POLYMORPHISM - Same interface, different behavior
class Cat(Animal):
def speak(self):
return "Meow!"
def make_speak(animal):
print(animal.speak()) # Works for any Animal
make_speak(Dog()) # Woof!
make_speak(Cat()) # Meow!
@staticmethod and @classmethod?Answer:
class MyClass:
class_variable = "I'm a class variable"
def instance_method(self):
# Has access to instance (self) and class
print(f"Instance method: {self}")
@classmethod
def class_method(cls):
# Has access to class, not instance
print(f"Class method: {cls.class_variable}")
@staticmethod
def static_method():
# No access to class or instance
print("Static method: No access to self or cls")
# Usage
obj = MyClass()
obj.instance_method() # Needs instance
MyClass.class_method() # Can call on class
MyClass.static_method() # Utility function
When to Use:
Answer: Dunder methods (double underscore) define behavior for built-in operations.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
# For print() and str()
return f"Point({self.x}, {self.y})"
def __repr__(self):
# For debugging, repr()
return f"Point({self.x!r}, {self.y!r})"
def __add__(self, other):
# For + operator
return Point(self.x + other.x, self.y + other.y)
def __eq__(self, other):
# For == operator
return self.x == other.x and self.y == other.y
def __len__(self):
# For len()
return 2
def __getitem__(self, index):
# For indexing []
if index == 0:
return self.x
elif index == 1:
return self.y
raise IndexError
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1 + p2) # Point(4, 6)
print(p1 == Point(1, 2)) # True
with statement and context managers?Answer: The with statement simplifies exception handling by automatically handling setup and
cleanup.
Built-in Example:
# Without with - must handle cleanup manually
file = open("file.txt")
try:
content = file.read()
finally:
file.close()
# With statement - automatic cleanup
with open("file.txt") as file:
content = file.read()
# File automatically closed
Creating Context Managers:
# Method 1: Using __enter__ and __exit__
class DatabaseConnection:
def __enter__(self):
print("Opening connection")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing connection")
# Return True to suppress exceptions
return False
def query(self, sql):
print(f"Executing: {sql}")
with DatabaseConnection() as db:
db.query("SELECT * FROM users")
# Method 2: Using contextlib
from contextlib import contextmanager
@contextmanager
def timer():
import time
start = time.time()
yield # Code inside 'with' block runs here
print(f"Time: {time.time() - start:.2f}s")
with timer():
# Some code to time
sum(range(1000000))
self keyword.Answer: self is a reference to the current instance of a class. It's used to access instance
attributes and methods.
class Person:
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age
def introduce(self):
# self allows access to instance data
return f"Hi, I'm {self.name}, {self.age} years old"
def celebrate_birthday(self):
self.age += 1 # Modifying instance attribute
return f"Happy Birthday! Now {self.age}"
# Creating instances
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
# self refers to different objects
print(person1.introduce()) # Hi, I'm Alice, 25 years old
print(person2.introduce()) # Hi, I'm Bob, 30 years old
Notes:
self is just a convention; you could use any nameself as first parameter in instance methodsAnswer: An iterator is an object that allows traversal through a container.
Iterator Protocol:
__iter__(): Returns the iterator object__next__(): Returns the next value# Creating an iterator
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# Using the iterator
for num in CountDown(5):
print(num) # 5, 4, 3, 2, 1
# Built-in functions create iterators
my_list = [1, 2, 3]
iterator = iter(my_list)
print(next(iterator)) # 1
print(next(iterator)) # 2
range() and xrange()?Answer: In Python 3, only range() exists and it behaves like Python 2's xrange().
| Feature | range() (Python 3) | Old range() (Python 2) |
|---|---|---|
| Returns | Range object (iterator) | List |
| Memory | Efficient (calculated on demand) | Stores all values |
| Speed | Fast for large ranges | Slow for large ranges |
# Python 3 range is memory efficient
big_range = range(1000000000) # Uses minimal memory
print(big_range[0]) # Calculates on demand
# You can still convert to list if needed
small_list = list(range(10)) # [0, 1, 2, ..., 9]
Answer:
import threading
import time
# Creating a thread
def task(name, delay):
for i in range(3):
time.sleep(delay)
print(f"Thread {name}: {i}")
# Method 1: Using Thread directly
thread1 = threading.Thread(target=task, args=("A", 0.5))
thread2 = threading.Thread(target=task, args=("B", 0.3))
thread1.start()
thread2.start()
thread1.join() # Wait for completion
thread2.join()
# Method 2: Subclassing Thread
class MyThread(threading.Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
for i in range(3):
time.sleep(0.5)
print(f"{self.name}: {i}")
# Thread synchronization
lock = threading.Lock()
counter = 0
def increment():
global counter
with lock: # Prevents race conditions
counter += 1
Answer: Pickling is serializing Python objects to bytes (for storage/transmission). Unpickling is the reverse.
import pickle
# Pickling - saving object
data = {
'name': 'John',
'scores': [90, 85, 88],
'passed': True
}
# Save to file
with open('data.pkl', 'wb') as f:
pickle.dump(data, f)
# Unpickling - loading object
with open('data.pkl', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data) # Same as original data
# Pickle to bytes
pickled_bytes = pickle.dumps(data)
unpickled = pickle.loads(pickled_bytes)
Security Warning: Never unpickle data from untrusted sources—it can execute arbitrary code.
Answer: __slots__ is used to explicitly declare data attributes, saving memory and preventing
dynamic attribute creation.
# Without slots
class RegularClass:
def __init__(self, x, y):
self.x = x
self.y = y
# With slots
class SlottedClass:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
# Regular class allows dynamic attributes
obj1 = RegularClass(1, 2)
obj1.z = 3 # Works
# Slotted class doesn't
obj2 = SlottedClass(1, 2)
obj2.z = 3 # AttributeError!
# Memory comparison
import sys
print(sys.getsizeof(obj1.__dict__)) # ~104 bytes
# SlottedClass has no __dict__, uses less memory
When to Use Slots:
Answer:
# calculator.py
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# test_calculator.py
import unittest
from calculator import add, divide
class TestCalculator(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
self.assertEqual(add(-1, 1), 0)
def test_add_floats(self):
self.assertAlmostEqual(add(0.1, 0.2), 0.3, places=5)
def test_divide(self):
self.assertEqual(divide(10, 2), 5)
def test_divide_by_zero(self):
with self.assertRaises(ValueError):
divide(10, 0)
def setUp(self):
# Runs before each test
print("Setting up")
def tearDown(self):
# Runs after each test
print("Tearing down")
if __name__ == '__main__':
unittest.main()
Using pytest (more popular):
# test_calc.py
import pytest
from calculator import add, divide
def test_add():
assert add(2, 3) == 5
def test_divide_by_zero():
with pytest.raises(ValueError):
divide(10, 0)
# Run with: pytest test_calc.py
Answer: Monkey patching is modifying a class or module at runtime.
# Original class
class MyClass:
def greeting(self):
return "Hello"
# Monkey patching - adding a method
def new_greeting(self):
return "Hi there!"
MyClass.greeting = new_greeting
obj = MyClass()
print(obj.greeting()) # "Hi there!"
# Monkey patching in testing
# Replacing external dependencies for testing
import requests
def mock_get(*args, **kwargs):
class MockResponse:
status_code = 200
text = "Mocked response"
return MockResponse()
# Save original
original_get = requests.get
# Patch
requests.get = mock_get
# Now requests.get returns mocked data
response = requests.get("any url")
print(response.text) # "Mocked response"
# Restore
requests.get = original_get
Answer: Metaclasses are classes for classes. They define how classes behave.
# Default metaclass is 'type'
# When you write:
class MyClass:
pass
# Python executes:
MyClass = type('MyClass', (), {})
# Creating a metaclass
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
print("Initializing database")
# Both variables point to same instance
db1 = Database() # "Initializing database"
db2 = Database() # Nothing printed - returns existing
print(db1 is db2) # True
__name__ variable?Answer: __name__ is a special variable that holds the name of the current module.
# my_module.py
print(f"__name__ is: {__name__}")
def main():
print("Main function running")
if __name__ == "__main__":
# This block runs only when file is executed directly
main()
When run directly:
python my_module.py
# Output: __name__ is: __main__
# Main function running
When imported:
import my_module
# Output: __name__ is: my_module
# (main() is NOT called)
append() and extend().Answer:
# append() adds ONE element to the end
list1 = [1, 2, 3]
list1.append([4, 5])
print(list1) # [1, 2, 3, [4, 5]]
# extend() adds EACH element from an iterable
list2 = [1, 2, 3]
list2.extend([4, 5])
print(list2) # [1, 2, 3, 4, 5]
# append with single item
list3 = [1, 2, 3]
list3.append(4)
print(list3) # [1, 2, 3, 4]
# extend with string (treats as iterable of chars)
list4 = [1, 2, 3]
list4.extend("ab")
print(list4) # [1, 2, 3, 'a', 'b']
Answer:
def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
@repeat(3)
def greet(name):
return f"Hello, {name}!"
print(greet("World"))
# ['Hello, World!', 'Hello, World!', 'Hello, World!']
Answer:
import asyncio
async def fetch_data(delay, data):
print(f"Start fetching {data}")
await asyncio.sleep(delay) # Non-blocking wait
print(f"Done fetching {data}")
return {"data": data}
async def main():
# Run concurrently
task1 = asyncio.create_task(fetch_data(2, "A"))
task2 = asyncio.create_task(fetch_data(1, "B"))
result1 = await task1
result2 = await task2
print(result1, result2)
asyncio.run(main())
Answer: MRO defines the order in which base classes are searched when looking for a method.
class A:
def method(self):
return "A"
class B(A):
def method(self):
return "B"
class C(A):
def method(self):
return "C"
class D(B, C):
pass
# MRO can be viewed with:
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d = D()
print(d.method()) # "B" (B comes before C in MRO)
Answer: Descriptors are objects that define __get__, __set__, or __delete__ methods to
customize attribute access.
class Validator:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, type=None):
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if not isinstance(value, int):
raise TypeError(f"{self.name} must be an integer")
if value < 0:
raise ValueError(f"{self.name} must be positive")
obj.__dict__[self.name] = value
class Person:
age = Validator()
def __init__(self, age):
self.age = age
p = Person(25) # Works
p = Person(-5) # ValueError!
functools module?Answer:
from functools import lru_cache, partial, reduce, wraps
# lru_cache - Memoization
@lru_cache(maxsize=100)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# partial - Pre-fill arguments
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
print(square(5)) # 25
# reduce - Accumulate values
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# wraps - Preserve function metadata in decorators
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# Method 1: Slicing (most Pythonic)
s = "hello"
reversed_s = s[::-1]
print(reversed_s) # "olleh"
# Method 2: reversed() + join()
reversed_s = ''.join(reversed(s))
# Method 3: Loop
reversed_s = ''
for char in s:
reversed_s = char + reversed_s
def find_duplicates(lst):
seen = set()
duplicates = set()
for item in lst:
if item in seen:
duplicates.add(item)
seen.add(item)
return list(duplicates)
# Alternative using Counter
from collections import Counter
def find_duplicates_counter(lst):
counts = Counter(lst)
return [item for item, count in counts.items() if count > 1]
print(find_duplicates([1, 2, 3, 2, 4, 3, 5])) # [2, 3]
def are_anagrams(s1, s2):
# Method 1: Sorting
return sorted(s1.lower()) == sorted(s2.lower())
def are_anagrams_counter(s1, s2):
# Method 2: Counter (more efficient)
from collections import Counter
return Counter(s1.lower()) == Counter(s2.lower())
print(are_anagrams("listen", "silent")) # True
print(are_anagrams("hello", "world")) # False
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self.items.pop()
def peek(self):
if self.is_empty():
raise IndexError("Stack is empty")
return self.items[-1]
def is_empty(self):
return len(self.items) == 0
def size(self):
return len(self.items)
def first_non_repeating(s):
from collections import Counter
counts = Counter(s)
for char in s:
if counts[char] == 1:
return char
return None
print(first_non_repeating("aabbcdef")) # 'c'
Answer: NumPy is a library for numerical computing with powerful array operations.
import numpy as np
# Creating arrays
arr = np.array([1, 2, 3, 4, 5])
matrix = np.array([[1, 2], [3, 4]])
# Operations are vectorized (fast)
print(arr * 2) # [2, 4, 6, 8, 10]
print(arr.mean()) # 3.0
# Useful for data science and ML
Answer: Pandas provides data structures (DataFrame, Series) for data manipulation and analysis.
import pandas as pd
# DataFrame creation
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie'],
'Age': [25, 30, 35],
'Salary': [50000, 60000, 70000]
})
# Operations
print(df[df['Age'] > 25]) # Filter
print(df.groupby('Name')['Salary'].mean()) # Aggregate
Difference: NumPy is for numerical arrays; Pandas is for tabular data with labels.
Answer:
| Feature | Flask | Django |
|---|---|---|
| Type | Micro-framework | Full-stack framework |
| Size | Lightweight | Batteries-included |
| ORM | External (SQLAlchemy) | Built-in |
| Admin | Manual | Automatic |
| Best for | APIs, small apps | Large applications |
Answer: A virtual environment is an isolated Python environment for managing project dependencies.
# Create
python -m venv myenv
# Activate (Windows)
myenv\Scripts\activate
# Activate (Mac/Linux)
source myenv/bin/activate
# Install packages
pip install requests
# Save dependencies
pip freeze > requirements.txt
# Install from requirements
pip install -r requirements.txt
Answer: PEP 8 is Python's official style guide for writing clean, readable code.
Key Rules:
snake_case for functions/variables, PascalCase for classesPreparing for tech interviews? Explore more resources on Sproutern for coding practice and career guidance.
Our team of career experts, industry professionals, and former recruiters brings decades of combined experience in helping students and freshers launch successful careers.
If you found this article helpful, please cite it as: