How would you characterize Python's approach to variable typing?
Answer
Python is dynamically typed. The interpreter assigns types based on what you assign to a variable at runtime, not through declarations.
These are the Python questions you'll encounter in technical interviews, each with a detailed written answer. Master Python fundamentals, then work through data structures, algorithms, and advanced language features.
Python is a high-level, dynamically typed language known for its simplicity and readability. It's widely used in web development, data science, machine learning, automation, and scripting, making it one of the most versatile programming languages.
Python interviews assess your grasp of core concepts like decorators, generators, list comprehensions, the GIL, and asynchronous programming. You'll discuss object-oriented design, memory management, and how to write Pythonic code.
Python proficiency is increasingly important for backend development, data science, and automation roles. Master the language and you'll be competitive for a wide range of positions.
Reading answers is not the same as giving them. Sit a full mock interview and get scored on how you actually explain them.
Try a mock interviewAnswer
Python is dynamically typed. The interpreter assigns types based on what you assign to a variable at runtime, not through declarations.
Answer
ref = nums creates a reference to the same list, not a copy. When you append to nums, ref reflects the change because both point to the same object.
Answer
Tuples are immutable sequences. Once created, their contents cannot be changed. Lists, dictionaries, and sets are all mutable.
Answer
The // operator performs floor division. 5//2 returns 2, not 2.5.
Answer
Negative indexing starts from the end. -1 is the last element, -2 is second-to-last, and -3 is third-to-last.
Answer
Empty collections evaluate to False in boolean context. Non-empty collections evaluate to True.
Answer
Python uses the 'import' keyword to load modules with syntax like 'import module_name' or 'from module_name import function_name'.
Answer
zip() pairs elements from multiple iterables by index, returning an iterator of tuples.
Answer
pass is a no-op statement used when syntax requires a statement but you don't want to execute anything. Commonly used in empty function definitions.
Answer
Use the 'in' operator to check for key existence. dict[key] would raise KeyError if missing. get() is useful when you want a default value.
Answer
Lists are mutable sequences using square brackets [1, 2, 3] where you can add, remove, or modify elements. Tuples are immutable sequences using parentheses (1, 2, 3) where contents cannot change after creation. Use lists for collections that grow or shrink over time. Use tuples for fixed data like coordinates or database records. Tuples can serve as dictionary keys because they're hashable; lists cannot.
Answer
Python uses call-by-object-reference: you pass a reference to the object, not a copy. For mutable objects like lists, modifications inside the function affect the original. For immutable types like integers and strings, reassigning the parameter only affects the local variable. Example: appending to a list parameter changes the caller's list, but reassigning the parameter name does not.
Answer
Decorators are functions that wrap other functions to add functionality. A decorator takes a function as input, adds behavior, and returns an enhanced version. Applied with the @ symbol. Common uses include logging, timing, authentication, and caching. Example: @timer decorator could measure execution time by recording start time before calling the function and elapsed time after.
Answer
Mutable types can change after creation with modifications happening in-place. Examples: lists, dictionaries, sets. Immutable types cannot change; modifications create new objects. Examples: strings, tuples, integers, floats, frozensets. This distinction matters for performance (in-place is faster) and functionality (immutable types can be dictionary keys).
Answer
Context managers automatically handle setup and cleanup of resources like files and network connections. The 'with' statement calls __enter__ when entering the block and guarantees __exit__ runs when leaving, even if exceptions occur. This prevents resource leaks and is cleaner than manual try/finally patterns. Example: 'with open(file) as f:' automatically closes the file.
Answer
Shallow copy creates a new object but inserts references to nested objects from the original. Changes to nested structures affect both copies. Deep copy creates a completely independent clone with all nested objects duplicated. Use shallow copy for flat structures with immutable elements. Use deep copy for nested structures with mutable elements to ensure true independence.
Answer
LEGB stands for Local, Enclosing, Global, Built-in. Python searches for a variable in this order: Local (inside current function), Enclosing (in outer functions), Global (module level), Built-in (like print, len). This explains why accessing global variables requires the 'global' keyword and why nested functions can access outer function variables.
Answer
List comprehension creates lists using concise syntax: [expression for item in iterable if condition]. Instead of explicit loops with append(), it expresses intent in one readable line. Advantages include better readability, faster execution due to optimized C-level implementation, and cleaner code. Example: [x**2 for x in range(10) if x % 2 == 0] replaces a 4-line loop.
Answer
The == operator checks if two objects have the same value (equality). The 'is' operator checks if they refer to the same object in memory (identity). Use 'is' when checking for None, True, False. Most comparisons use ==. A common mistake: using 'is' with numbers or strings where Python caches certain values, making 'is' appear to work unpredictably.
Answer
self is a convention for the first parameter in instance methods, referring to the instance the method is called on. Unlike languages where instance reference is implicit, Python makes it explicit. When you call dog.bark(), Python automatically passes dog as self. While technically any name works, using 'self' is universal convention. Forgetting it causes TypeError when calling methods.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd start by adding print statements at key points to trace variable values and execution flow. For more systematic debugging, I'd use pdb by inserting 'import pdb; pdb.set_trace()' to step through code line by line. I'd check assumptions about input data first, then verify each calculation. If dealing with multiple calls, I'd isolate the failing case. For production code, I'd use the logging module instead of print statements to avoid leaving debug code.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I once needed to filter temperature readings above a threshold and convert them from Celsius to Fahrenheit in one step. Using comprehension: [c * 9/5 + 32 for c in readings if c > threshold] was much cleaner than a traditional loop that would need append calls. It was more readable for other developers and ran slightly faster. The intent was immediately clear from one line rather than four.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd start with a concrete example: show them creating two lists and assigning one to the other, then modifying the original and observing the assigned copy changes too. I'd explain it's like having two name tags pointing to the same object in memory. Then I'd contrast with strings by showing that reassigning a string doesn't affect other references. The key insight is that mutable objects are modified in place, while immutable objects require creating new objects.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
First, I'd profile the code to identify the actual bottleneck rather than guessing. I'd use the timeit module or cProfile to measure which functions consume the most time. Common optimizations include using list comprehensions instead of loops, replacing nested loops with dictionaries for lookups, or using generator expressions for memory efficiency. I'd also consider algorithmic improvements like avoiding redundant calculations or using better data structures for the access patterns.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
Module-level scope contains variables defined at the top of a file, accessible throughout. Local scope is inside a function, accessible only there. If a function modifies a module-level variable, it needs the 'global' keyword. Example: a counter variable at module level could track how many times a function was called, but only if the function declares it global. Without that, it would create a local variable instead.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd wrap the API call in try/except to catch specific exceptions like ConnectionError or TimeoutError separately from unexpected errors. I'd retry failed requests with exponential backoff to handle temporary failures gracefully. I'd log errors for debugging while returning user-friendly messages. I'd also validate the response structure before processing to catch malformed data. For production code, I'd use a retry library to avoid reinventing this logic.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd make the balance a private attribute with the __ prefix to prevent direct modification. I'd provide public methods like deposit() and withdraw() that validate inputs and maintain invariants like preventing negative balances. I'd add a getter for reading balance without modification. This enforces business rules in one place and prevents accidental data corruption. I might also add logging to track all balance changes for auditing.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd identify common patterns and extract them into utility functions or classes that don't depend on project-specific details. I'd document them well with docstrings and type hints for clarity. I'd consider creating a shared module or package that multiple projects import, versioning it appropriately. I'd write comprehensive tests to ensure reliability. For functions, I'd make them general-purpose with parameters for customization rather than hardcoding values.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
Generators produce values one at a time using yield instead of creating everything in memory. For processing large files or data streams, generators consume minimal memory since only one item is in memory at a time. They're ideal for creating data pipelines where later stages process output from earlier stages. You'd use generators for potentially infinite sequences like sensor readings, whereas you'd use lists for small, fixed collections you need random access to.
This browser cannot record audio. Answer the question out loud anyway, then compare yourself against the model answer below.
Answer
I'd start by exploring the documentation and writing small test examples to understand it deeply. Then I'd create a brief presentation with practical examples relevant to our codebase, not abstract demos. I'd prepare sample code they could run immediately and point out gotchas I discovered. I'd encourage questions and create follow-up resources. I'd lead a code review session applying it to real work so the team sees the value immediately rather than just learning theory.
def reverse_string(text):
# Your implementation here
passAnswer
def reverse_string(text):
return text[::-1]
# Or using a loop:
def reverse_string_loop(text):
result = ''
for char in text:
result = char + result
return resultdef find_duplicate_ids(user_ids):
# Your implementation here
pass
# Example usage:
ids = [101, 202, 101, 303, 202, 404]
# Should return [101, 202]Answer
def find_duplicate_ids(user_ids):
from collections import Counter
counts = Counter(user_ids)
return [uid for uid, count in counts.items() if count > 1]
# Or using a set:
def find_duplicate_ids_alt(user_ids):
seen = set()
duplicates = set()
for uid in user_ids:
if uid in seen:
duplicates.add(uid)
seen.add(uid)
return list(duplicates)def flatten_orders(orders_structure):
# Your implementation here
pass
# Example:
orders = [['item1', 'item2'], ['item3', ['item4', 'item5']]]
# Should return ['item1', 'item2', 'item3', 'item4', 'item5']Answer
def flatten_orders(orders_structure):
result = []
for item in orders_structure:
if isinstance(item, list):
result.extend(flatten_orders(item))
else:
result.append(item)
return resultdef find_matching_prices(prices, target):
# Your implementation here
pass
# Example:
prices = [25, 50, 75, 100]
target = 125
# Should return [1, 2] (indices where 50 + 75 = 125)Answer
def find_matching_prices(prices, target):
seen = {}
for i, price in enumerate(prices):
complement = target - price
if complement in seen:
return [seen[complement], i]
seen[price] = i
return Nonedef generate_fibonacci(n):
# Your implementation here
pass
# Example:
# generate_fibonacci(6) should return [0, 1, 1, 2, 3, 5]Answer
def generate_fibonacci(n):
if n <= 0:
return []
if n == 1:
return [0]
fib = [0, 1]
for i in range(2, n):
fib.append(fib[i-1] + fib[i-2])
return fib[:n]def is_valid_sku(sku):
# Your implementation here
pass
# Examples:
# is_valid_sku('A1B-2-B1A') should return True
# is_valid_sku('SKU-1234') should return FalseAnswer
def is_valid_sku(sku):
cleaned = ''.join(char.lower() for char in sku if char.isalnum())
return cleaned == cleaned[::-1]def merge_invoice_lists(list1, list2):
# Your implementation here
pass
# Example:
# merge_invoice_lists([10, 30, 50], [15, 25, 40])
# Should return [10, 15, 25, 30, 40, 50]Answer
def merge_invoice_lists(list1, list2):
result = []
i, j = 0, 0
while i < len(list1) and j < len(list2):
if list1[i] <= list2[j]:
result.append(list1[i])
i += 1
else:
result.append(list2[j])
j += 1
result.extend(list1[i:])
result.extend(list2[j:])
return resultdef count_keywords(descriptions):
# Your implementation here
pass
# Example:
# count_keywords('purchase sale Purchase refund Sale')
# Should return {'purchase': 2, 'sale': 2, 'refund': 1}Answer
def count_keywords(descriptions):
from collections import Counter
words = descriptions.lower().split()
return dict(Counter(words))def remove_duplicate_employees(employee_ids):
# Your implementation here
pass
# Example:
# remove_duplicate_employees([1001, 1002, 1001, 1003, 1002])
# Should return [1001, 1002, 1003]Answer
def remove_duplicate_employees(employee_ids):
seen = set()
result = []
for emp_id in employee_ids:
if emp_id not in seen:
result.append(emp_id)
seen.add(emp_id)
return resultdef transpose_table(table):
# Your implementation here
pass
# Example:
# Input: [[1, 2, 3], [4, 5, 6]]
# Output: [[1, 4], [2, 5], [3, 6]]Answer
def transpose_table(table):
return [list(row) for row in zip(*table)]
# Or using list comprehension:
def transpose_table_alt(table):
if not table:
return []
return [[table[i][j] for i in range(len(table))] for j in range(len(table[0]))]