Functions Deep Dive Quiz
Quiz
Question 1 of 46
(0 answered)
Question 1
What are
name and 'Alice' called in this code?def greet(name): # Function definition
return f"Hello {name}"
greet('Alice') # Function callWhat will this code output?
✓
Correct!
Parameters are variables in the function definition (
name). Arguments are the actual values passed when calling the function ('Alice').✗
Incorrect
Parameters are variables in the function definition (
name). Arguments are the actual values passed when calling the function ('Alice').Think about which one is in the definition and which one is passed when calling.
Question 2
What is the output of the following code?
def get_user():
return 'Alice', 30, 'alice@example.com'
print(get_user())What will this code output?
✓
Correct!
Functions can return multiple values as a tuple. The
get_user() function returns three values packed into a tuple.✗
Incorrect
Functions can return multiple values as a tuple. The
get_user() function returns three values packed into a tuple.Think about what happens when a function has multiple return values separated by commas.
Question 3
What will this code print?
def greet(name, greeting="Hello"):
return f"{greeting} {name}"
print(greet("Bob", "Hi"))What will this code output?
✓
Correct!
The function has a default parameter
greeting="Hello", but when we pass "Hi" as the second positional argument, it overrides the default value.✗
Incorrect
The function has a default parameter
greeting="Hello", but when we pass "Hi" as the second positional argument, it overrides the default value.Default parameters can be overridden by passing explicit values.
Question 4
Which of the following are valid ways to call this function:
def func(a, b, /, c, d)?✓
Correct!
Parameters before
/ must be positional-only. So a and b cannot be passed as keyword arguments. Options 1 and 2 correctly use positional arguments for a and b.✗
Incorrect
Parameters before
/ must be positional-only. So a and b cannot be passed as keyword arguments. Options 1 and 2 correctly use positional arguments for a and b.The
/ separator forces parameters before it to be positional-only.Question 5
What keyword is used to modify a variable from an outer (enclosing) function’s scope?
✓
Correct!
The
nonlocal keyword tells Python to use a variable from the enclosing function’s scope, allowing you to modify it.✗
Incorrect
The
nonlocal keyword tells Python to use a variable from the enclosing function’s scope, allowing you to modify it.It’s not ‘global’, which is for module-level variables.
Question 6
Type hints in Python are enforced at runtime and will raise errors if wrong types are passed.
✓
Correct!
Type hints are NOT enforced at runtime in Python. They are purely for documentation and static type checkers like mypy. The code
greet(123, "thirty") will execute without errors even with type hints.✗
Incorrect
Type hints are NOT enforced at runtime in Python. They are purely for documentation and static type checkers like mypy. The code
greet(123, "thirty") will execute without errors even with type hints.Consider whether Python checks types automatically or if you need external tools.
Question 7
What is the output of this code?
def sum_all(*args):
return sum(args)
print(type(sum_all(1, 2, 3)))What will this code output?
✓
Correct!
While
*args collects arguments into a tuple, the function returns sum(args), which is an integer (6). The type() call is on the return value, not on args itself.✗
Incorrect
While
*args collects arguments into a tuple, the function returns sum(args), which is an integer (6). The type() call is on the return value, not on args itself.What does the sum() function return?
Question 8
Arrange these parameter types in the correct order they must appear in a function definition:
Drag to arrange in the correct order
⋮⋮
Positional parameters
⋮⋮
**kwargs
⋮⋮
Keyword parameters with defaults
⋮⋮
*args
✓
Correct!
Python requires this specific order: positional → *args → keyword → **kwargs. For example:
def func(a, b, *args, x=10, y=20, **kwargs)✗
Incorrect
Python requires this specific order: positional → *args → keyword → **kwargs. For example:
def func(a, b, *args, x=10, y=20, **kwargs)Question 9
According to the LEGB rule, which scope is searched FIRST when looking up a variable?
✓
Correct!
The LEGB rule searches in order: Local → Enclosing → Global → Built-in. Python always starts with the innermost (Local) scope first.
✗
Incorrect
The LEGB rule searches in order: Local → Enclosing → Global → Built-in. Python always starts with the innermost (Local) scope first.
Think about which scope is ‘closest’ to where the variable is being used.
Question 10
What will this code print?
def make_multiplier(n):
def multiply(x):
return x * n
return multiply
times2 = make_multiplier(2)
times3 = make_multiplier(3)
print(times2(5) + times3(5))What will this code output?
✓
Correct!
This is a closure.
times2 remembers n=2, so times2(5) = 10. times3 remembers n=3, so times3(5) = 15. Total: 10 + 15 = 25.✗
Incorrect
This is a closure.
times2 remembers n=2, so times2(5) = 10. times3 remembers n=3, so times3(5) = 15. Total: 10 + 15 = 25.Each function returned by make_multiplier remembers its own value of n.
Question 11
A closure is created automatically when an inner function references a variable from its outer function’s scope.
✓
Correct!
Closures happen automatically in Python when an inner function uses variables from an outer function. You don’t need to do anything special—just reference the outer variable.
✗
Incorrect
Closures happen automatically in Python when an inner function uses variables from an outer function. You don’t need to do anything special—just reference the outer variable.
Think about what happens when multiply(x) uses ’n’ from make_multiplier.
Question 12
What keyword is used in a generator function to pause execution and return a value?
✓
Correct!
yield produces a value and pauses the generator; next() resumes execution and returns that value to the caller.✗
Incorrect
yield produces a value and pauses the generator; next() resumes execution and returns that value to the caller.It’s a 5-letter word that sounds like ‘giving up control temporarily’.
Question 13
What is the output?
def yield_example():
yield 1
return 2
yield 3
gen = yield_example()
print(next(gen))What will this code output?
✓
Correct!
The generator yields 1 first. The
return 2 statement will stop the generator (raising StopIteration with value 2), and yield 3 is never reached.✗
Incorrect
The generator yields 1 first. The
return 2 statement will stop the generator (raising StopIteration with value 2), and yield 3 is never reached.What does the first yield statement produce?
Question 14
Which statements about generators are TRUE?
✓
Correct!
Statements 1, 2, and 3 are true. However, once a generator is exhausted, it stays exhausted—you need to create a new generator to start over.
✗
Incorrect
Statements 1, 2, and 3 are true. However, once a generator is exhausted, it stays exhausted—you need to create a new generator to start over.
Think about what happens after a generator has yielded all its values.
Question 15
Complete the code to make this a tail-recursive function:
Fill in the missing function name
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return _____(n - 1, n * acc)✓
Correct!
Tail recursion occurs when the recursive call is the last operation. The function must call itself with updated parameters.
✗
Incorrect
Tail recursion occurs when the recursive call is the last operation. The function must call itself with updated parameters.
Question 16
What is the main difference between
return and yield in a function?✓
Correct!
return exits the function completely and loses all local state. yield pauses the function, preserving its state, and can resume execution when called again.✗
Incorrect
return exits the function completely and loses all local state. yield pauses the function, preserving its state, and can resume execution when called again.Think about whether you can continue execution after the statement.
Question 17
Python automatically optimizes tail recursion to prevent stack overflow.
✓
Correct!
Python does NOT optimize tail recursion. Unlike some languages (like Scheme), Python doesn’t convert tail-recursive calls into loops, so you still risk hitting the recursion limit.
✗
Incorrect
Python does NOT optimize tail recursion. Unlike some languages (like Scheme), Python doesn’t convert tail-recursive calls into loops, so you still risk hitting the recursion limit.
Review the ‘Tail Recursion’ section about Python’s behavior.
Question 18
What will this code print?
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count)What will this code output?
✓
Correct!
The
global keyword allows the function to modify the module-level count variable. Each call increments it, so after two calls: 0 → 1 → 2.✗
Incorrect
The
global keyword allows the function to modify the module-level count variable. Each call increments it, so after two calls: 0 → 1 → 2.What does the ‘global’ keyword allow you to do?
Question 19
Why should you avoid mutable default parameters like
def append_to(item, lst=[])?✓
Correct!
The default list
[] is created once when the function is defined, not each time it’s called. All calls share the same list object, leading to unexpected behavior.✗
Incorrect
The default list
[] is created once when the function is defined, not each time it’s called. All calls share the same list object, leading to unexpected behavior.Think about when the default value is created.
Question 20
What is the LEGB rule?
What is the LEGB rule?
Local → Enclosing → Global → Built-in
The order in which Python searches for variables:
- Local: Inside current function
- Enclosing: Inside outer functions
- Global: Module level
- Built-in: Python built-ins
Did you get it right?
✓
Correct!
✗
Incorrect
Question 21
When should you use
global vs nonlocal?When should you use
global vs nonlocal?global: Modify module-level variables from within a function
nonlocal: Modify variables from an outer (enclosing) function’s scope
Both should be used sparingly. Prefer passing parameters and returning values instead.
Did you get it right?
✓
Correct!
✗
Incorrect
Question 22
Predict the output:
def outer():
x = "enclosing"
def inner():
x = "local"
print(x)
inner()
print(x)
outer()What will this code output?
✓
Correct!
Inside
inner(), the assignment x = "local" creates a new local variable, which is printed first. The outer() function’s x remains unchanged at “enclosing”.✗
Incorrect
Inside
inner(), the assignment x = "local" creates a new local variable, which is printed first. The outer() function’s x remains unchanged at “enclosing”.Each function has its own local scope. Assignment creates a new local variable.
Question 23
Which are valid reasons to use recursion over iteration?
✓
Correct!
Recursion is best when problems are naturally recursive (option 1) and when it improves code clarity (option 3). However, recursion is typically NOT faster (option 2) and CAN cause stack overflow (option 4).
✗
Incorrect
Recursion is best when problems are naturally recursive (option 1) and when it improves code clarity (option 3). However, recursion is typically NOT faster (option 2) and CAN cause stack overflow (option 4).
Think about the advantages mentioned in ‘Why Use Recursion?’
Question 24
Complete the code to unpack the dictionary as keyword arguments:
Fill in the operator needed
def add(a, b, c):
return a + b + c
values = {"a": 1, "b": 2, "c": 3}
result = add(___values)✓
Correct!
Use
** to unpack a dictionary as keyword arguments. This passes a=1, b=2, c=3 to the function.
Remember: * spreads values by position, ** spreads values by name.✗
Incorrect
Use
** to unpack a dictionary as keyword arguments. This passes a=1, b=2, c=3 to the function.
Remember: * spreads values by position, ** spreads values by name.Question 25
What is the default recursion depth limit in Python?
✓
Correct!
Python’s default recursion limit is typically 1000. You can check it with
sys.getrecursionlimit() and change it with sys.setrecursionlimit().✗
Incorrect
Python’s default recursion limit is typically 1000. You can check it with
sys.getrecursionlimit() and change it with sys.setrecursionlimit().It’s mentioned in the ‘Recursion Limit’ section.
Question 26
What is the output?
def func(a, b, *args, x=10, **kwargs):
return len(args) + len(kwargs)
print(func(1, 2, 3, 4, x=100, y=200, z=300))What will this code output?
✓
Correct!
args captures extra positional arguments (3, 4) → length 2. kwargs captures extra keyword arguments (y=200, z=300) → length 2. Total: 2 + 2 = 4.✗
Incorrect
args captures extra positional arguments (3, 4) → length 2. kwargs captures extra keyword arguments (y=200, z=300) → length 2. Total: 2 + 2 = 4.Count how many values go into args and kwargs separately.
Question 27
Generator expressions use square brackets
[] like list comprehensions.✓
Correct!
Generator expressions use parentheses
(), not square brackets. List comprehensions use []. Example: (x**2 for x in range(10)) is a generator.✗
Incorrect
Generator expressions use parentheses
(), not square brackets. List comprehensions use []. Example: (x**2 for x in range(10)) is a generator.Think about the syntax difference between lazy and eager evaluation.
Question 28
What decorator can be used to automatically cache recursive function results?
✓
Correct!
The
@lru_cache decorator from functools automatically caches function results, making recursive functions like Fibonacci much faster by avoiding redundant calculations.✗
Incorrect
The
@lru_cache decorator from functools automatically caches function results, making recursive functions like Fibonacci much faster by avoiding redundant calculations.It’s imported from the functools module.
Question 29
What happens when you run this code?
def count_up_to(n):
count = 1
while count <= n:
yield count
count += 1
gen = count_up_to(3)
print(next(gen))
print(next(gen))
print(list(gen))What will this code output?
✓
Correct!
The first
next() yields 1, the second yields 2. When we convert to list, the generator continues from where it left off, yielding only 3 (the remaining value).✗
Incorrect
The first
next() yields 1, the second yields 2. When we convert to list, the generator continues from where it left off, yielding only 3 (the remaining value).Generators remember their state. list() consumes what’s left.
Question 30
Which of the following demonstrate closures?
✓
Correct!
Closures occur when inner functions access variables from enclosing scopes (options 1 and 3). Option 2 uses global scope (not a closure). Option 4 alone doesn’t create a closure unless the returned function uses outer variables.
✗
Incorrect
Closures occur when inner functions access variables from enclosing scopes (options 1 and 3). Option 2 uses global scope (not a closure). Option 4 alone doesn’t create a closure unless the returned function uses outer variables.
Closures involve inner functions accessing outer function variables.
Question 31
What method is used to get the next value from a generator?
✓
Correct!
The
next() built-in function is used to get the next value from a generator. It raises StopIteration when the generator is exhausted.✗
Incorrect
The
next() built-in function is used to get the next value from a generator. It raises StopIteration when the generator is exhausted.It’s a built-in function with 4 letters.
Question 32
When you define a function with a docstring, you can access it using the
__doc__ attribute.✓
Correct!
Docstrings (the triple-quoted strings at the start of functions) are stored in the
__doc__ attribute and can be accessed programmatically or with the help() function.✗
Incorrect
Docstrings (the triple-quoted strings at the start of functions) are stored in the
__doc__ attribute and can be accessed programmatically or with the help() function.Think about what happens to the triple-quoted string in a function.
Question 33
In the function definition
def func(a, /, b, *, c), which parameter can be passed BOTH positionally and as a keyword?✓
Correct!
Parameter
a is positional-only (before /), c is keyword-only (after *), but b is in the middle and can be passed either way.✗
Incorrect
Parameter
a is positional-only (before /), c is keyword-only (after *), but b is in the middle and can be passed either way.Look for the parameter that’s neither before / nor after *.
Question 34
What will this code print?
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
c1 = make_counter()
c2 = make_counter()
print(c1())
print(c1())
print(c2())What will this code output?
✓
Correct!
Each call to
make_counter() creates a NEW closure with its own count variable. c1 has its own counter (1→2), and c2 has a separate counter starting at 1.✗
Incorrect
Each call to
make_counter() creates a NEW closure with its own count variable. c1 has its own counter (1→2), and c2 has a separate counter starting at 1.Each counter function has its own independent count variable.
Question 35
What is memoization in the context of recursive functions?
What is memoization in the context of recursive functions?
Memoization is an optimization technique that caches the results of expensive function calls.
When a recursive function is called with the same arguments again, it returns the cached result instead of recalculating.
This dramatically improves performance for recursive algorithms like Fibonacci that have overlapping subproblems.
Did you get it right?
✓
Correct!
✗
Incorrect
Question 36
Arrange these steps for exception handling in the correct execution order:
Drag to arrange in the order Python executes them
⋮⋮
try block (attempt code)
⋮⋮
else block (runs if no exception)
⋮⋮
except block (handle errors if they occur)
⋮⋮
finally block (always runs)
✓
Correct!
Python executes: try → except (if error) → else (if no error) → finally (always). The finally block always runs regardless of whether an exception occurred.
✗
Incorrect
Python executes: try → except (if error) → else (if no error) → finally (always). The finally block always runs regardless of whether an exception occurred.
Question 37
Complete the generator function to read a file line by line:
Fill in the missing keyword
def read_large_file(file_path):
with open(file_path, 'r') as f:
for line in f:
_____ line.strip()✓
Correct!
Using
yield makes this a generator that produces lines one at a time, which is memory-efficient for large files.✗
Incorrect
Using
yield makes this a generator that produces lines one at a time, which is memory-efficient for large files.Question 38
Which approach is generally MORE memory-efficient for processing large datasets?
✓
Correct!
Generator expressions are lazy—they create values on demand. List comprehensions create the entire list in memory immediately. For large datasets, generators are much more memory-efficient.
✗
Incorrect
Generator expressions are lazy—they create values on demand. List comprehensions create the entire list in memory immediately. For large datasets, generators are much more memory-efficient.
Think about when the values are created and stored.
Question 39
A function can have multiple
yield statements but only one return statement.✓
Correct!
A function can have multiple
return statements (though only one executes). Similarly, a generator can have multiple yield statements, and they will all execute in sequence as the generator is consumed.✗
Incorrect
A function can have multiple
return statements (though only one executes). Similarly, a generator can have multiple yield statements, and they will all execute in sequence as the generator is consumed.Think about early returns and multiple yield points in a generator.
Question 40
Compare loop behavior vs function behavior. What’s the output?
# With loop
data = "hello"
for i in range(2):
data = "goodbye"
print(data)
# With function
data = "hello"
def modify():
data = "goodbye"
modify()
print(data)What will this code output?
✓
Correct!
Loop: Assignment modifies the OUTER data → prints
goodbye. Function: Assignment creates a NEW local variable → outer data unchanged → prints hello. Loops don’t create scope; functions do.✗
Incorrect
Loop: Assignment modifies the OUTER data → prints
goodbye. Function: Assignment creates a NEW local variable → outer data unchanged → prints hello. Loops don’t create scope; functions do.Which one creates a new scope for variables?
Question 41
What will this code print?
data = "hello"
for i in range(3):
data = data + "!"
print(data)What will this code output?
✓
Correct!
The loop modifies the OUTER
data variable (doesn’t create a new one). Each iteration: hello → hello! → hello!! → hello!!!. The modified value persists after the loop.✗
Incorrect
The loop modifies the OUTER
data variable (doesn’t create a new one). Each iteration: hello → hello! → hello!! → hello!!!. The modified value persists after the loop.Does the loop create a new ‘data’ variable or modify the existing one?
Question 42
Where does a variable belong in Python?
✓
Correct!
A variable belongs to the scope where it’s first assigned (not where it’s used). This is why
data = data.replace(...) modifies an outer variable if data was assigned outside the loop.✗
Incorrect
A variable belongs to the scope where it’s first assigned (not where it’s used). This is why
data = data.replace(...) modifies an outer variable if data was assigned outside the loop.Think about when Python decides which scope a variable belongs to.
Question 43
Complete the loop so the function iterates over the list passed in:
Fill in the blank — what do you loop over inside the function?
def count_queries(_____):
result = {}
for item in _____:
key = item.strip().lower()
result[key] = result.get(key, 0) + 1
return result
queries = ['nike', ' Nike', 'rebook']
print(count_queries(queries))✓
Correct!
Both blanks must use the same name — whatever you name the parameter in
def count_queries(_____), you must loop over that same name inside. The name itself is arbitrary (items, datapoints, query_list — all valid). The actual list (queries) only comes in at the call site: count_queries(queries). Python then binds your parameter to that list, so inside the function you always use the parameter name, not queries.✗
Incorrect
Both blanks must use the same name — whatever you name the parameter in
def count_queries(_____), you must loop over that same name inside. The name itself is arbitrary (items, datapoints, query_list — all valid). The actual list (queries) only comes in at the call site: count_queries(queries). Python then binds your parameter to that list, so inside the function you always use the parameter name, not queries.Both blanks are the same word — the parameter name you choose in the definition.
Question 44
Complete the code to call the function with the list:
Fill in the missing function call
def normalize(items):
return [item.strip().lower() for item in items]
queries = [' Nike', ' REBOOK', 'nike']
print(__________(queries))✓
Correct!
Call the function by name and pass the list:
normalize(queries). Wrapping it in print(...) prints the result immediately — no need for a separate variable. You could also do result = normalize(queries) then print(result), both are valid.✗
Incorrect
Call the function by name and pass the list:
normalize(queries). Wrapping it in print(...) prints the result immediately — no need for a separate variable. You could also do result = normalize(queries) then print(result), both are valid.Question 45
What is the main reason to wrap code in
if __name__ == "__main__":?✓
Correct!
When a file is imported, Python sets
__name__ to the module name, so the guarded block is skipped. When the file is run directly, __name__ equals "__main__" and the block executes. This lets you write files that are both importable libraries and runnable scripts without unwanted side effects on import.✗
Incorrect
When a file is imported, Python sets
__name__ to the module name, so the guarded block is skipped. When the file is run directly, __name__ equals "__main__" and the block executes. This lets you write files that are both importable libraries and runnable scripts without unwanted side effects on import.Think about what happens when another script does
import yourfile.Question 46
Complete the script by adding the guard block so the main logic only runs when executed directly:
Fill in the blank
def normalize(items):
return [item.strip().lower() for item in items]
def main():
queries = [' Nike', ' REBOOK', 'nike']
print(normalize(queries))
__________✓
Correct!
Python automatically sets
__name__ to '__main__' when you run a file directly (python script.py). When the same file is imported by another module, __name__ is set to the file’s module name instead. The full guard block if __name__ == '__main__': main() ensures main() only runs on direct execution, not on import.✗
Incorrect
Python automatically sets
__name__ to '__main__' when you run a file directly (python script.py). When the same file is imported by another module, __name__ is set to the file’s module name instead. The full guard block if __name__ == '__main__': main() ensures main() only runs on direct execution, not on import.The guard block starts with
if __name__ == '__main__': and calls main() inside it.Quiz Results
Score
0/0
Accuracy
0%
Right
0
Wrong
Skipped
0
Last updated on