This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.
Identity, Image, and Privacy Notice
To respect individual privacy, some names, profile photographs, avatars, biographical details, and other identifying information displayed in this guide may be replaced with pseudonyms, licensed stock images, illustrative avatars, composite images, or representative descriptions. Unless a person is expressly identified as an actual contributor, a displayed name, image, or profile should not be understood as depicting or identifying a specific candidate, interviewer, employee, or other real individual. These representations are provided for editorial and illustrative purposes only and do not imply endorsement, employment, participation, or affiliation with this guide or any company mentioned in it. Any resemblance to an actual person is coincidental.
Company Notice
This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.
Questions or comments?
Contact us for general questions, or share feedback, technical corrections, and comments with the community.
11. What is a Python namespace?Language SpecificEasy
i Question Details
Explain what a namespace maps, identify local, enclosing, global, and built-in namespaces, and describe how namespaces reduce naming conflicts.
Short Interview Answer (30-60 seconds)
A Python namespace is a mapping from names to objects. For an unqualified name, Python normally searches the local, enclosing, global, and built in namespaces in that order. Separate namespaces let functions and modules reuse the same name without automatically changing one another.
Detailed Explanation
A Python namespace maps names to objects. After count = 5, the current namespace binds the name count to the integer object 5. The name is not the object itself.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
For an unqualified name inside a function, Python normally follows the local, enclosing, global, and built in lookup order. The local namespace belongs to the current function call. Enclosing namespaces belong to outer functions. The global namespace belongs to the current module. The built in namespace provides names such as print and len.
Namespaces reduce conflicts because separate functions and modules can use the same name independently. Assignment inside a function normally makes that name local to the function. The global statement allows assignment to a module global name. The nonlocal statement allows assignment to a name in an enclosing function.
One important edge case is that assigning to a name anywhere in a function normally makes it local throughout that function. Reading it before the assignment can raise UnboundLocalError.
Namespace entries require memory for name bindings, and lookup adds a small implementation dependent runtime cost. In production code, clear local names, explicit imports, and limited mutable global state make behavior easier to understand and test.
Where it is used
Namespaces are used whenever Python executes modules, functions, class bodies, imports, and built in operations. Function namespaces keep parameters and temporary values separate for each active call. Module namespaces organize functions, classes, constants, and imported names. Class bodies use a namespace to collect names that become class attributes. In production applications, namespaces help separate code across modules and reduce accidental naming conflicts.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands how Python binds names to objects and resolves names at runtime. It also tests knowledge of scope, shadowing, function behavior, module organization, and the risks of unclear shared state.
Common interview mistakes
A common mistake is saying that a variable directly contains an object. In Python, a name is bound to an object through a namespace. Another mistake is assuming that assignment inside a function changes a global name. It normally creates or updates a local binding unless global is declared. Developers may also hide built in names by assigning names such as list, str, or len. Another mistake is treating namespace and scope as the same thing. A namespace stores bindings, while a scope defines where a name can be resolved. It is also incorrect to assume that the local namespace is shared by every call to the same function.
Interview tip
Start by saying that a namespace maps names to objects. Then explain the local, enclosing, global, and built in lookup order. Finish with one practical point about shadowing or about two functions safely using the same local name.
Interviewer may ask next
Why can reading a local name before assigning it raise UnboundLocalError?
It raises UnboundLocalError because assignment to that name anywhere in the function normally makes the name local throughout the function. Python then tries to read the local binding before it has received a value. This matters because a global name with the same spelling will not be used for that read. The code must assign the local value first or explicitly declare global or nonlocal when that is the intended behavior.
What is the tradeoff of storing mutable application state in a module global namespace?
Module global state is easy to access, but mutable global values create shared state that can be changed from many places. This can make tests, concurrent code, and debugging harder because behavior depends on hidden changes. Module globals are reasonable for functions, classes, imported names, and constants, while frequently changing application state is usually clearer when passed explicitly or managed by a dedicated object.
12. What are *args and **kwargs used for?Language SpecificEasy
i Question Details
Explain how *args collects extra positional arguments, how **kwargs collects extra keyword arguments, and how both forms are used for unpacking during function calls.
Short Interview Answer (30-60 seconds)
*args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments into a dictionary. In a function call, * unpacks an iterable into positional arguments, and ** unpacks a mapping into keyword arguments. I use these forms when the number of values can vary or when forwarding arguments, but I prefer explicit parameters when the expected inputs are known.
*args and **kwargs let a function receive additional arguments when their exact number is not fixed. In a function definition, *args collects unmatched positional arguments into a tuple. Positional arguments are matched by their order. **kwargs collects unmatched keyword arguments into a dictionary. Keyword arguments are passed using names.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
The names args and kwargs are conventions. The star symbols create the behavior. Even when no extra values are passed, the function receives an empty tuple for *args and an empty dictionary for **kwargs.
The syntax works in the opposite direction during a call. One star reads values from an iterable and passes them as separate positional arguments. Two stars read a mapping and pass its entries as separate keyword arguments. Keyword names produced by ** must be strings.
These forms are useful in wrappers, decorators, callbacks, configuration helpers, and functions that forward arguments. However, they can hide which inputs are accepted. Explicit parameters are clearer when the interface is known.
Python must process every collected or unpacked value, so call setup time and temporary memory grow with the number of arguments. Duplicate keyword names and invalid keyword keys raise TypeError.
Example
The example defines a function with one required parameter, extra positional parameters, and extra keyword parameters. The name parameter receives the required value. scores receives the remaining positional values as a tuple. details receives the remaining keyword values as a dictionary. The first call passes each value directly. The second call uses * to unpack a list into positional arguments and ** to unpack a dictionary into keyword arguments. Both calls give the function the same values and produce the same output.
Code
defshow_student(name, *scores, **details):
# name receives the required positional argument.print(f"Name: {name}")
# scores is a tuple of extra positional arguments.print(f"Scores: {scores}")
# details is a dictionary of extra keyword arguments.print(f"Details: {details}")
# Pass each value directly.
show_student(
"Amina",
88,
92,
course="Python",
active=True,
)
print()
# Store positional values in a list.
stored_scores = [88, 92]
# Store keyword values in a dictionary.
stored_details = {
"course": "Python",
"active": True,
}
# One star unpacks the list into positional arguments.# Two stars unpack the dictionary into keyword arguments.
show_student("Amina", *stored_scores, **stored_details)
Where it is used
This feature is used in wrapper functions that pass arguments to another function, decorators that preserve a wrapped function call, callback systems, logging helpers with optional context, constructors with optional settings, and reusable utilities that accept varying inputs. It is also useful when values are already stored in a list, tuple, generator, or dictionary and must be passed to a function. In production code, explicit parameters are usually better when the accepted inputs are known because they improve readability, validation, documentation, editor support, and type checking.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands flexible function parameters, positional and keyword argument matching, and unpacking during function calls. It also tests whether the candidate knows when this flexibility is useful and when explicit parameters would create a clearer production interface.
Common interview mistakes
A common mistake is thinking args and kwargs are required names. They are only conventions. The star symbols create the behavior. Another mistake is expecting *args to be a list, but it is a tuple. kwargs is a dictionary. Developers may also use flexible parameters when named parameters would make the interface clearer. During a call, duplicate keyword names raise TypeError. Values passed with must come from a mapping, and every resulting keyword name must be a string. Unpacking an iterable with * also consumes its values, so a generator may be exhausted after the call.
Interview tip
Explain the two directions clearly. In a function definition, the stars collect extra arguments. In a function call, the stars unpack stored values. Then state that *args is a tuple, **kwargs is a dictionary, and explicit parameters are better when the accepted inputs are known.
Interviewer may ask next
What happens if the same keyword is supplied directly and through ** unpacking?
Python raises TypeError because the call supplies more than one value for the same keyword. For example, passing course directly and also unpacking a mapping containing course creates a duplicate. This matters when keyword data comes from several sources, so the values should be merged and checked before the call.
What is the tradeoff of using *args and **kwargs in production code?
They provide flexibility, but they make the accepted interface less obvious. They are useful for wrappers, decorators, callbacks, and argument forwarding. Explicit parameters are better when the inputs are known because they improve readability, validation, documentation, editor support, and type checking. There is also call setup cost because Python must collect or unpack each supplied value.
13. What is a lambda function?Language SpecificEasy
i Question Details
Explain lambda syntax, its single-expression restriction, suitable use cases, and when a named function is clearer.
Short Interview Answer (30-60 seconds)
A lambda function is a small anonymous function created with the lambda keyword. It can accept multiple arguments, but its body contains one expression. Python evaluates that expression and returns the result automatically. I use a lambda for a short operation passed to a function such as sorted. I use a named function when the logic needs several steps, reuse, testing, or a clear name.
Detailed Explanation
Use a lambda when you need a very small function for a short and clear operation. A lambda function is created with the lambda keyword. Arguments appear before a colon, and one expression appears after it. For example, lambda value: value * 2 creates a function that returns twice the given value.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
A lambda does not need a name when it is created, but Python still creates a normal function object. The function can be stored, passed to another function, or called later. Python evaluates the single expression and returns its result automatically. Statements such as return, try, and normal assignment statements cannot appear in the body. Some expression forms, including a conditional expression, are allowed, but complex expressions reduce readability.
Lambdas are useful as short key functions for sorted, min, and max, or as small callbacks. A named function is clearer when logic needs several steps, reuse, documentation, type hints, testing, or error handling. A lambda has no special speed advantage over def. Creating either form allocates a function object. If a lambda closes over outside variables, it keeps references to them, which can extend the lifetime of those objects.
Where it is used
Lambda functions are commonly used as key functions when sorting records, selecting a minimum or maximum item, or grouping values by one field. They can also be used for small callbacks and simple transformations passed directly to another function. A named function is better when the logic is reused, needs several steps, requires annotations or documentation, needs clear error handling, or should be tested separately.
Why Interviewers Ask This
Interviewers ask this question to check whether the candidate understands Python function syntax, expression evaluation, function objects, closures, and readable coding choices. They also want to know whether the candidate can choose between a short lambda and a named function in production code.
Common interview mistakes
A common mistake is thinking that lambda creates a special or faster kind of function. Python creates a normal function object, and lambda has no special speed benefit. Another mistake is trying to place statements such as return, try, or a normal assignment statement inside the body. Only one expression is allowed. Developers may also place complex conditional logic inside a lambda, which makes the code hard to read. Another important mistake appears when lambdas created inside a loop close over the same changing variable. The variable is looked up when the function is called, so every lambda may see the final loop value unless the current value is captured with a default argument.
Interview tip
Start by saying that a lambda is a small anonymous function with one expression. Explain that the expression result is returned automatically. Give one practical use such as a sorting key. Finish by saying that a named function is clearer for complex, reusable, documented, or separately tested logic.
Interviewer may ask next
What happens when lambdas created in a loop use the loop variable?
They normally look up the loop variable when each function is called, not when each function is created. Because the lambdas close over the same variable, they may all return a result based on its final value. This matters when creating callbacks in a loop. The current value can be captured by placing it in a default argument, but a named function may be clearer when the behavior is not obvious.
Does using a lambda improve performance or memory use compared with def?
No, a lambda does not provide a general performance or memory advantage over def. Both forms create function objects, and their execution cost depends mainly on the work performed by the function. A closure created by either form can keep references to outside objects and extend their lifetime. The main tradeoff is readability and convenience, not speed or memory savings.
14. What is a list comprehension?Language SpecificEasy
i Question Details
Explain list-comprehension syntax with optional filtering, compare it with an equivalent loop, and discuss when a comprehension becomes too complex to remain readable.
Short Interview Answer (30-60 seconds)
A list comprehension is a concise way to create a new list from an iterable. Its basic form is [expression for item in iterable], and it can include an optional filter such as [expression for item in iterable if condition]. It creates the complete list immediately. I use it for one clear transformation and perhaps one simple condition. I use a normal loop when the logic needs several steps or becomes difficult to read.
Detailed Explanation
A list comprehension creates a new list by iterating over an iterable and evaluating an expression for each selected item. Its basic syntax is [expression for item in iterable]. An optional filter appears after the for clause, as in [number * number for number in numbers if number % 2 == 0].
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
For numbers equal to [1, 2, 3, 4, 5, 6], Python visits each value in order. It tests whether the number is even. When the condition is true, it squares the number and adds the result to the new list. The output is [4, 16, 36]. An equivalent loop starts with an empty list, checks the same condition, and calls append with the same expression.
For this example, both forms take linear time because they inspect every input value. If n values are checked and k results are kept, the new list uses space proportional to k, not counting the internal size of newly created result objects.
A comprehension is best for simple transformation and filtering. Use a normal loop for complex conditions, several actions, logging, error handling, or logic that needs clear intermediate steps.
Where it is used
List comprehensions are useful when production code needs a new list made from existing data. Examples include selecting active records, converting strings to numbers, extracting fields from objects, normalizing API values, and preparing display data. They are a good choice when each item has one clear transformation and perhaps one simple condition. A normal loop is usually clearer when the work includes logging, exception handling, several temporary values, multiple actions, or complex business rules. A generator expression may be better when the full result does not need to be stored in memory at once.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands Python iteration, filtering, expression evaluation, and list creation. They also want to see whether the candidate can choose readable code instead of using compact syntax when the logic is too complex.
Common interview mistakes
A common mistake is putting the filter before the for clause. A filtering condition belongs after the iterable clause. Another mistake is using a comprehension only for side effects, such as printing values or changing unrelated state. A comprehension should normally create useful result values. Developers may also place several conditions, nested loops, or long expressions inside one comprehension, which can make valid code difficult to understand. Another mistake is assuming that a comprehension changes the original list. It creates a new outer list. However, if the new and original lists contain references to the same mutable objects, changing one shared object can be visible through both lists. An empty iterable is not an error. It simply produces an empty list. If the expression or condition raises an exception, list creation stops and the exception is propagated.
Interview tip
Start with the basic syntax. Show one example with a filter. Explain the equivalent loop and state that both create the same result. Then mention eager list creation, linear work for a simple single loop, and the readability rule: use a normal loop when the comprehension becomes hard to understand.
Interviewer may ask next
Does a list comprehension modify the original list?
No, a list comprehension creates a new outer list. It reads values from the original iterable and stores the expression results in a separate list. This matters because adding or removing items from the new outer list does not change the original outer list. The limitation is that both lists can still refer to the same mutable inner objects, so changing one shared object may be visible through both lists.
When should a generator expression be used instead of a list comprehension?
Use a generator expression when values can be processed one at a time and the complete result does not need to be stored immediately. A generator expression produces values lazily, which can reduce memory use for large inputs. The tradeoff is that it does not provide a ready list, it is normally consumed during iteration, and operations that require indexing, repeated traversal, or a stored result may still require converting it to a list.
15. What is None in Python?Language SpecificEasy
i Question Details
Explain that None is a singleton representing the absence of a value, how it differs from false, zero, and an empty collection, and how it should be compared.
Short Interview Answer (30-60 seconds)
None is the single Python object used to represent the absence of a value. It is different from False, zero, an empty string, and an empty collection, even though all of them are treated as false in a condition. I compare a value with None by using is or is not because the check is about whether the value refers to the exact None object.
Detailed Explanation
Use None when a value is missing, unknown, not provided, or has no useful result. None is the only instance of NoneType, so every reference to None points to the same object.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
None is not equal to False, zero, an empty string, or an empty collection. These values are all false in a truth value test, but they can still contain valid information. For example, zero can be a valid count, and an empty list can be a valid result with no items.
Compare a value with None by writing value is None or value is not None. The is operator checks object identity. Writing value == None may call custom equality behavior defined by the value's class, so it is less clear and can produce unexpected behavior.
A function that reaches its end without an explicit return statement returns None. A return statement with no expression also returns None.
The identity check takes constant time and does not create another None object. Python reuses the singleton. In production, None is commonly used for optional arguments, missing database values, cache misses, and operations that do not return useful data. Use a direct None check when other false values are valid.
Where it is used
None is used for optional function arguments, values that have not been loaded, missing configuration values, database fields with no value, cache misses, and functions that perform an action without returning useful data. A direct None check is important when zero, False, an empty string, or an empty collection is valid data. When None itself is a valid input, production code can use a separate sentinel object to distinguish a missing argument from an explicitly supplied None.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands object identity, truth value testing, function return behavior, and the difference between missing data and valid false values. It also tests whether the candidate uses the correct comparison syntax in production code.
Common interview mistakes
A common mistake is writing value == None instead of value is None. Another mistake is assuming that None is the same as False, zero, an empty string, or an empty collection. Developers also sometimes write if not value when they only want to detect None. That condition also matches every other false value. Another mistake is forgetting that a function without an explicit returned expression produces None. It is also incorrect to use None as a default argument when the application must distinguish an omitted argument from an argument explicitly set to None.
Interview tip
Start by saying that None represents the absence of a value and is the only instance of NoneType. Then separate it from other false values and state the comparison rule clearly: use is None or is not None.
Interviewer may ask next
What does a Python function return when it has no return statement?
It returns None. Python produces None when execution reaches the end of the function without an explicit return statement. A return statement with no expression has the same behavior. This matters because callers may need to distinguish an operation with no useful result from one that returns a real value.
What should you use when None is a valid argument value but you also need to detect an omitted argument?
Use a separate sentinel object as the default value. The sentinel represents an omitted argument, while None remains a valid value supplied by the caller. This adds one private object and an identity check, but it removes ambiguity and makes the function behavior reliable.
16. What is PEP 8?Language SpecificEasy
i Question Details
Explain the purpose of Python's style guide and discuss its guidance on indentation, naming, imports, whitespace, and line length.
Short Interview Answer (30-60 seconds)
PEP 8 is the main style guide for Python code. It recommends four spaces for each indentation level, clear naming conventions, organized imports, consistent whitespace, and a maximum line length of 79 characters. Most of its guidance improves readability and does not change runtime behavior. Indentation is the important exception because Python uses indentation to define code blocks. In a real project, I follow the established project style when it differs from PEP 8.
Detailed Explanation
PEP 8 is the main style guide for Python code. Its practical goal is to make code easier to read, review, and maintain. Most of its rules are conventions, not requirements enforced by the Python interpreter.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
PEP 8 recommends four spaces for each indentation level. Spaces are preferred over tabs. This matters because Python uses indentation to define code blocks, so inconsistent indentation can change program structure or cause an error.
Function and variable names normally use lowercase words separated by underscores. Class names normally use capitalized words. Constants normally use uppercase words separated by underscores.
Imports usually appear near the top of the file. They should normally be written on separate lines and grouped as standard library imports, third party imports, and local application imports.
PEP 8 also recommends avoiding unnecessary whitespace, while placing spaces around most binary operators. It limits code lines to 79 characters and comments or documentation text to 72 characters. A team may agree on a code limit up to 99 characters. Project consistency takes priority when a documented local convention differs. Following PEP 8 has no inherent runtime or memory cost, although formatters and linters use development and build resources when they run.
Where it is used
PEP 8 is used when teams write application code, libraries, command line tools, tests, and automation scripts. It guides code reviews and helps developers understand unfamiliar files more quickly. Projects often use editor settings, formatters, linters, and continuous integration checks to apply selected conventions consistently. These tools operate during development or validation and do not normally add work or memory use to the running application.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands Python coding conventions and can write code that is readable, consistent, and easy for a team to maintain. It also tests whether the candidate can separate style recommendations from syntax rules that affect how Python interprets a program.
Common interview mistakes
A common mistake is saying that Python requires every PEP 8 rule. Most rules are recommendations for readability. Another mistake is treating indentation as only visual style. Python uses indentation to form code blocks, and mixing tabs with spaces for indentation can raise a TabError when the meaning is inconsistent. Other mistakes include putting several unrelated imports on one line, failing to separate import groups, using unclear names, adding spaces inside brackets, adding spaces around keyword argument equals signs, and enforcing the 79 character limit even when a documented project convention uses another limit. Developers should not change stable code only to satisfy style rules when the change adds risk or reduces clarity.
Interview tip
Begin by defining PEP 8 as Python's main style guide. Cover four spaces for indentation, naming, import grouping, whitespace, and line length. Then explain that most rules improve readability rather than runtime behavior, while indentation can affect syntax and program structure. Mention that an established project convention takes priority within that project.
Interviewer may ask next
Does Python reject code that does not follow PEP 8?
No, Python does not reject code merely because it breaks most PEP 8 conventions. A long line, an unusual variable name, or poorly grouped imports can still run. Indentation is different because Python uses it to define blocks. Invalid indentation can raise an IndentationError, and inconsistent use of tabs and spaces can raise a TabError. This distinction matters because style problems mainly affect maintainability, while indentation problems can affect correctness.
Should a production team always enforce the 79 character limit?
No, a production team may adopt a documented wider limit. PEP 8 permits teams that agree on the choice to increase the code line limit up to 99 characters, while comments and documentation text should remain limited to 72 characters. A wider limit can reduce unnecessary wrapping, but longer lines can be harder to review beside another file. The important production decision is to choose one rule and enforce it consistently with project tooling.
17. What is the difference between sort() and sorted()?Language SpecificEasy
i Question Details
Explain in-place sorting versus returning a new list, accepted iterable types, return values, and use of key and reverse parameters.
Short Interview Answer (30-60 seconds)
Use list.sort() when I want to change an existing list. It sorts that list in place and returns None. Use sorted() when I want a new sorted list or need to sort another iterable such as a tuple, set, dictionary, or generator. Both accept key for choosing the comparison value and reverse for controlling the sort direction.
The practical choice is simple. Use list.sort() when changing the existing list is acceptable. Use sorted() when the original data must remain unchanged or the input is not a list.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
The sort() method exists only on lists. It rearranges the items inside the same list object and returns None. Returning None helps show that the method performs a change instead of creating a result value.
The sorted() function accepts any iterable. This includes lists, tuples, sets, dictionaries, and generators. It reads the iterable and returns a new list. For a dictionary, it sorts the keys unless another iterable is provided.
Both forms accept key and reverse. Python calls key once for each item and sorts using the returned values. Setting reverse=True produces descending order. Python sorting is stable, so items with equal key values keep their original relative order.
Sorting is usually O(n log n), but already ordered data can be closer to O(n). sorted() needs O(n) memory for its new list. list.sort() avoids that second result list, but the sorting process can still use temporary memory. Items must also have values that Python can compare.
Example
The example uses one employee list as the shared source data. sorted() creates a new list ordered by score and leaves the source list unchanged. A separate copy is then sorted with list.sort(), which changes that copy directly and returns None. Both operations use the score field as the key and reverse=True to place higher scores first.
Code
employees = [
{"name": "Mina", "score": 82},
{"name": "Arun", "score": 95},
{"name": "Lina", "score": 88},
]
# sorted() reads the iterable and creates a new list.# The original employees list keeps its current order.
employees_by_score = sorted(
employees,
key=lambda employee: employee["score"],
reverse=True,
)
print("Original list:", employees)
print("New list from sorted():", employees_by_score)
# Create a separate list so the shared source data is not changed.
employees_copy = employees.copy()
# sort() changes the existing list object and returns None.
sort_result = employees_copy.sort(
key=lambda employee: employee["score"],
reverse=True,
)
print("List changed by sort():", employees_copy)
print("Return value from sort():", sort_result)
Where it is used
Use list.sort() when a program owns a list and no longer needs its previous order, such as arranging records before processing them. Use sorted() when the original order is still needed, when the data is shared with other code, or when the input is a tuple, set, dictionary, generator, or another iterable. The key parameter is useful for sorting records by fields such as name, date, priority, score, or price. In production code, sorted() is often safer when changing the original collection could create unexpected behavior elsewhere.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands mutation, return values, iterable handling, sorting options, and memory tradeoffs in Python. It also tests whether the candidate can avoid changing shared data by accident or assigning the None value returned by list.sort().
Common interview mistakes
A common mistake is writing result = values.sort() and expecting result to contain the sorted list. The result is None because sort() changes values directly. Another mistake is calling sort() on a tuple, set, dictionary, or generator. Only lists provide this method. Use sorted() for other iterables. Developers may also forget that sorted() always returns a list, even when the input is another type. The key function does not replace the items. It only provides the comparison value. Sorting values that cannot be compared can raise TypeError. A key function should return values that are mutually comparable. Finally, calling sort() on a shared list can change what other parts of the program observe.
Interview tip
Begin with the main difference. Say that list.sort() changes one list and returns None, while sorted() accepts any iterable and returns a new list. Then mention key, reverse, stable ordering, comparable values, and the memory tradeoff.
Interviewer may ask next
What happens when two items have the same key value?
They keep their original relative order because Python sorting is stable. This behavior matters when data was already ordered by another field. A later sort can group items by a new key without changing the earlier order among items whose new keys are equal.
Which option is better when memory usage matters?
Use list.sort() when the input is already a list and changing it is acceptable. It avoids allocating a second full result list, although the sorting process can still use temporary memory. Use sorted() when preserving the original data or accepting a general iterable is more important than the additional list allocation.
18. What is unpacking in Python?Language SpecificEasy
i Question Details
Explain iterable unpacking, starred targets, swapping values, nested unpacking, and errors caused by mismatched numbers of values.
Short Interview Answer (30-60 seconds)
Unpacking lets Python take values from an iterable and assign them to several targets in one statement. Normal unpacking requires the number and structure of the values to match the targets. A starred target can collect remaining values into a new list. Unpacking is useful for fixed records, swapping values, and nested data, but it should be used carefully when the input shape may change.
Detailed Explanation
Unpacking lets Python assign items from an iterable to several targets in one statement. For example, name, age = ("Amina", 30) assigns one value to each target. Python iterates over the object and requires the values to match the target structure.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
Normal unpacking raises ValueError when there are too few or too many values. A starred target handles a variable number of values. In first, *middle, last = values, Python assigns the first and last items normally and stores the remaining items in a new list named middle. Only one starred target is allowed at the same assignment level.
Python also supports swapping with left, right = right, left. Python evaluates the right side first, then assigns those results to the targets. Nested unpacking works when the target shape matches the data shape, such as name, (city, country) = user.
Use unpacking for stable records, function results, dictionary item iteration, and clear data extraction. Avoid deep or fragile unpacking when external data may change. Unpacking processes the required items, so its time cost grows with the number of values read. A starred target also allocates a new list for the collected values.
Where it is used
Unpacking is commonly used when reading fixed tuple results, receiving several values from a function, iterating through dictionary key and value pairs, separating fields from validated records, swapping variables, and extracting values from nested data. A starred target is useful when some positions are fixed and the remaining values should be collected. In production code, unpacking is clearest when the input structure is known and stable.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands assignment with iterables, exact value matching, starred targets, nested structures, and the errors Python raises when the structure does not match. It also tests whether the candidate knows when unpacking improves clarity and when explicit validation is safer.
Common interview mistakes
Common mistakes include using a different number of targets and values, which raises ValueError, and using more than one starred target at the same assignment level, which causes SyntaxError. Another mistake is assuming that a starred target preserves the original iterable type. Python stores the collected values in a new list. Nested unpacking also fails with ValueError when the data shape does not match the target shape. Deep unpacking can reduce readability and make changing input formats harder to handle.
Interview tip
Begin by saying that unpacking assigns iterable values to several targets. Then explain exact matching, starred targets, swapping, nested unpacking, and ValueError. Mention that a starred target creates a new list and that unpacking is safest when the input shape is stable.
Interviewer may ask next
What happens when the number or structure of values does not match the targets?
Python raises ValueError when normal unpacking receives too few or too many values. It also raises ValueError when a nested value does not match the nested target structure. This matters because unpacking assumes a known shape, so uncertain external data should be validated before assignment.
What are the performance and memory costs of a starred target?
A starred target reads the iterable and stores the collected values in a new list. The time cost grows with the number of values processed, and the extra memory cost grows with the number of values collected. This is convenient for small or expected records, but direct iteration can use less memory when the remaining input is very large.
19. What is the difference between local and global variables?Language SpecificEasy
i Question Details
Explain where local and global names are created, their visibility and lifetime, and when the global declaration is required for assignment.
Short Interview Answer (30-60 seconds)
A local variable is a name created inside a function and is normally visible only in that function call. A global variable is a name stored in the module namespace. A function can read a global name without a declaration. It needs the global declaration only when it will assign a new value to that name. Mutating an existing global object does not require global when the name itself is not reassigned. In production code, I usually prefer function arguments and return values because they make dependencies clear and testing easier.
The practical rule is to use local names for function work and limit changes to global state.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
A name assigned inside a function is local by default. It is visible inside that function call. Its binding disappears when the call ends, although the referenced object can remain alive if another reference still points to it.
A global name is stored in the module namespace. Code in the module and functions in that module can read it. The name normally remains available while the module remains loaded.
A function does not need global to read a global name. It needs global when an assignment should rebind that module level name. Without the declaration, Python treats an assigned name as local throughout the function. Reading it before the local assignment then raises UnboundLocalError.
The global statement is not needed when code only mutates an existing global object, such as appending to a list, because the name is not rebound. Assignment binds a name to an object and does not copy that object. Local and global name access normally has small constant time cost, although local lookup is generally simpler. Mutable global state can make testing and concurrent execution harder to control.
Example
The example creates request_count in the module namespace. The read_count function reads that global name without a global declaration. The record_request function uses global because it assigns a new integer to request_count. The local name message exists only inside that function call. The recent_requests list is also global, but appending to it does not require global because the code mutates the existing list instead of assigning a different object to the name. The assignments only bind names to objects and do not copy the referenced objects.
Code
# Create names in the module namespace.
request_count = 0
recent_requests = []
defread_count():
# Reading a global name does not require global.return request_count
defrecord_request(request_name):
# Assignment must update the module level request_count name.global request_count
# Integers are immutable, so this creates a new integer and rebinds the name.
request_count = request_count + 1# Appending mutates the existing global list.# No global declaration is needed because the list name is not reassigned.
recent_requests.append(request_name)
# This name is local to the current function call.
message = f"Recorded request {request_count}: {request_name}"return message
print(read_count())
print(record_request("health check"))
print(record_request("user profile"))
print(read_count())
print(recent_requests)
Where it is used
Local variables are used for request data, validation results, temporary calculations, loop state, and values needed during one function call. Global names are often used for constants, configuration created when a module loads, shared clients, and carefully controlled caches. Read only global constants are usually simple to manage. Mutable global state should be limited because one call can affect later calls, tests, or concurrent work. Function arguments, return values, and instance attributes usually make ownership and dependencies clearer.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands Python scope and name binding. They want to know whether the candidate can predict which name a function will read, when assignment creates a local name, when global is required, and how shared state can affect production code.
Common interview mistakes
A common mistake is using global just to read a global name. Reading alone does not require it. Another mistake is assigning to a global name without declaring it global. Python then treats that name as local throughout the function, which can cause UnboundLocalError. Developers may also think that mutating a global list requires global. It does not unless the list name itself is reassigned. Another mistake is assuming that global means one shared namespace for the entire application. A global name belongs to a specific module namespace. Excessive mutable global state can also create test isolation problems and unexpected behavior during concurrent execution.
Interview tip
Start with the main rule. Names assigned inside a function are local by default. Then explain that reading a module level name needs no declaration, while rebinding it requires global. Mention that mutating an existing global object is different from rebinding its name. Finish by explaining why explicit arguments and return values are usually safer in production.
Interviewer may ask next
What happens if a function reads and then assigns to a global name without using global?
Python treats the name as local throughout that function because the function contains an assignment to it. Reading the name before the local value has been assigned raises UnboundLocalError. This matters because Python decides the scope from the function code, not from the order in which branches happen to run.
Why does appending to a global list not require global, while replacing the list does?
Appending changes the existing list object, so the global name continues to point to the same object and no global declaration is required. Replacing the list assigns a different object to the name, so global is required when that assignment should update the module namespace. Mutation can be convenient, but shared mutable objects make testing and concurrent access harder to control.
20. What are default function arguments?Language SpecificEasy
i Question Details
Explain when default expressions are evaluated, how callers can omit corresponding arguments, and why mutable default values can cause shared-state bugs.
Short Interview Answer (30-60 seconds)
Default function arguments let callers omit selected arguments because the function already has stored values for them. Python evaluates each default expression when the def statement runs, not each time the function is called. Immutable defaults such as numbers, strings, and None are usually safe. For a list, dictionary, or set that should be new for every call, I use None and create the object inside the function.
The practical rule is simple. Use a fixed immutable value as a default. Use None when each call needs a new mutable object.
Useful Questions to Ask the Interviewer
Should I focus on Python language behavior, or also explain the runtime and standard library?
Which Python version and execution environment should I assume?
Would you like a small code example together with production tradeoffs and edge cases?
A default function argument is a parameter that has a value in the function definition. When the caller omits that argument, Python uses the stored default value. The caller can still provide another value.
Python evaluates a default expression when execution reaches the def statement. The resulting object is stored with the function. If the def statement runs again, such as when an enclosing function is called again, new defaults are created for that new function object.
This behavior matters for mutable objects. A list, dictionary, or set can change after creation. If one is used as a default and the function changes it, later calls that omit the argument reuse the same object and can see earlier changes.
The safe pattern is to use None and create a new object inside the function. This adds one small allocation for each omitted call. Memory is separate for each created object and grows only with the data stored in it. When None is a valid input, use a unique sentinel object instead.
Example
The function uses None as the stored default. When the caller omits items, the function creates a new list for that call. The first call returns a list containing apple. The second call returns a different list containing banana. When the caller provides an existing list, the function uses and changes that exact list. The final two printed values both contain orange and grape because they refer to the same provided list.
Code
defadd_item(item, items=None):
# None is the stored default value.# Create a new list only when the caller omits items.if items isNone:
items = []
# Add the requested value to the selected list.
items.append(item)
return items
# These calls each receive a separate new list.
first_result = add_item("apple")
second_result = add_item("banana")
print(first_result)
print(second_result)
# A list provided by the caller is used directly.
existing_items = ["orange"]
third_result = add_item("grape", existing_items)
print(third_result)
print(existing_items)
Where it is used
Default arguments are useful for optional settings such as retry counts, timeout values, formatting choices, logging flags, and dependency options. Immutable defaults such as integers, strings, booleans, tuples containing immutable values, and None are common. In production code, functions that collect records, build result lists, or update dictionaries should create a fresh mutable object for each call unless shared state is intentional, documented, and tested. Creating a new empty list or dictionary has a small constant time and memory cost, which is normally safer than keeping accidental shared state.
Why Interviewers Ask This
Interviewers ask this question to check whether a candidate understands when Python evaluates default expressions, how omitted arguments are handled, and why mutable defaults can create shared state. It also tests whether the candidate can choose a safe implementation for production code.
Common interview mistakes
A common mistake is believing that Python evaluates a default expression for every call. Another mistake is using an empty list, dictionary, or set as a default and changing it inside the function. Developers may also write if not items when they only want to detect an omitted value. That condition also treats an intentionally empty list as missing. Use if items is None when None is the marker. Another mistake is assuming a function call used as a default runs on every call. It runs when execution reaches the def statement. Mutable defaults can be used intentionally for shared state or caching, but that behavior should be explicit, documented, and carefully tested.
Interview tip
Start with the main rule that Python evaluates default expressions when the def statement runs. Then explain that callers may omit those arguments. Finish with the mutable default bug and the None pattern. This clearly covers the behavior, the risk, and the safe solution.
Interviewer may ask next
What happens if a function uses an empty list as its default value?
The same list is reused by calls that omit the argument. Python created and stored that list when execution reached the def statement. If one call changes it, a later call can see the earlier data. This matters because it creates hidden shared state. Use None and create a new list inside the function when calls need independent data.
What should you use when None is a valid argument value?
Use a unique sentinel object as the default. Create it once with object(), then compare the argument with that sentinel by using is. This separates an omitted argument from an explicit None value. The tradeoff is a little more code, but the function preserves the real meaning of None while still detecting whether the caller supplied the argument.
More questions load as you scroll
Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.