Quiz#

This quiz covers the key concepts from Lecture 4: Function Fundamentals, including function definition and calling, arguments (positional, default, keyword, *args, **kwargs), scopes (LEGB), pass-by-assignment, type hints, docstrings, and recursion.

Note

Instructions:

  • Answer all questions to the best of your ability.

  • Multiple choice questions have exactly one correct answer.

  • True/False questions require you to determine if the statement is correct.

  • Essay questions require short written responses (2-4 sentences).

  • Click the dropdown after each question to reveal the answer.


Multiple Choice#

Question 1

What is the output of the following code?

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

print(greet("Alice"))
  1. "Hello, Alice!"

  2. "Alice, Hello!"

  3. TypeError

  4. "None, Alice!"

Answer

A"Hello, Alice!"

The greeting parameter has a default value of "Hello". Since only name is provided, the default is used.

Question 2

What is the output of the following code?

def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item("a"))
print(add_item("b"))
  1. ['a'] then ['b']

  2. ['a'] then ['a', 'b']

  3. ['a', 'b'] then ['a', 'b']

  4. TypeError

Answer

B['a'] then ['a', 'b']

This is the mutable default argument trap. The default list [] is created once when the function is defined and shared across all calls. Each call appends to the same list object.

Question 3

What does the *args parameter collect?

  1. All keyword arguments as a dictionary.

  2. All positional arguments as a tuple.

  3. All arguments as a list.

  4. Only the first extra argument.

Answer

B – All positional arguments as a tuple.

*args collects any extra positional arguments into a tuple. Keyword arguments are collected by **kwargs into a dictionary.

Question 4

What is the output of the following code?

x = 10

def modify():
    x = 20
    print(x)

modify()
print(x)
  1. 20 then 20

  2. 20 then 10

  3. 10 then 10

  4. UnboundLocalError

Answer

B20 then 10

The x = 20 inside modify() creates a local variable that shadows the global x. The global x remains 10.

Question 5

In the LEGB rule, what does the “E” stand for?

  1. External

  2. Enclosing

  3. Environment

  4. Evaluated

Answer

B – Enclosing

LEGB stands for Local, Enclosing, Global, Built-in. The enclosing scope refers to the scope of an outer function when using nested functions.

Question 6

What is the output of the following code?

def func(a: int, b: str) -> bool:
    return str(a) == b

result = func(42, "42")
print(result)
  1. True

  2. False

  3. TypeError

  4. "42"

Answer

ATrue

Type hints are not enforced at runtime. str(42) returns "42", which equals the second argument "42", so the function returns True.

Question 7

What happens when you pass a list to a function and the function appends an element to it?

  1. The original list is unchanged because Python uses pass-by-value.

  2. The original list is modified because lists are mutable and passed by assignment.

  3. The original list is replaced with a new list.

  4. A TypeError is raised.

Answer

B – The original list is modified because lists are mutable and passed by assignment.

Python passes a reference to the list object. Since lists are mutable, in-place operations like append() modify the original object.

Question 8

What is the output of the following code?

def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner()

print(outer())
  1. 0

  2. 1

  3. NameError

  4. None

Answer

B1

nonlocal count allows inner() to modify count in the enclosing scope. It increments from 0 to 1, and outer() returns the result of inner().

Question 9

What is the output of the following code?

def combine(*args, **kwargs):
    return (args, kwargs)

print(combine(1, 2, x=3, y=4))
  1. ([1, 2], {'x': 3, 'y': 4})

  2. ((1, 2), {'x': 3, 'y': 4})

  3. (1, 2, 3, 4)

  4. TypeError

Answer

B((1, 2), {'x': 3, 'y': 4})

*args collects positional arguments 1, 2 as a tuple, and **kwargs collects keyword arguments as a dictionary.

Question 10

Which of the following is NOT true about Python type hints?

  1. They improve code readability and documentation.

  2. They are enforced at runtime by the Python interpreter.

  3. They enable static analysis tools like mypy.

  4. They can specify Optional for values that may be None.

Answer

B – They are enforced at runtime by the Python interpreter.

Type hints are not enforced at runtime. They serve as documentation and enable static analysis tools like mypy, but Python ignores them during execution.

Question 11

What is the output of the following code?

def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

print(factorial(5))
  1. 5

  2. 15

  3. 120

  4. RecursionError

Answer

C120

factorial(5) = 5 * 4 * 3 * 2 * 1 = 120. Each recursive call reduces n by 1 until the base case n <= 1 returns 1.

Question 12

What is the output of the following code?

def modify(data):
    data = [99, 99, 99]

original = [1, 2, 3]
modify(original)
print(original)
  1. [99, 99, 99]

  2. [1, 2, 3]

  3. []

  4. None

Answer

B[1, 2, 3]

Reassigning data = [99, 99, 99] inside the function creates a new local binding. The original list referenced by original is not affected because reassignment does not mutate the object.

Question 13

What does a function return if it has no return statement?

  1. 0

  2. An empty string ""

  3. None

  4. It raises an error.

Answer

CNone

Functions without an explicit return statement (or with a bare return) implicitly return None.

Question 14

Which correctly unpacks a function’s return value?

def get_coords():
    return 3.5, 7.2
  1. x, y = get_coords()

  2. (x, y) = get_coords()

  3. Both A and B are correct.

  4. Neither A nor B is correct.

Answer

C – Both A and B are correct.

Python supports tuple unpacking with or without parentheses. get_coords() returns a tuple, and both syntaxes correctly unpack the two values.

Question 15

What is the correct order of parameters in a function signature?

  1. def func(*args, a, b, **kwargs):

  2. def func(a, b, *args, **kwargs):

  3. def func(**kwargs, a, b, *args):

  4. def func(a, **kwargs, b, *args):

Answer

Bdef func(a, b, *args, **kwargs):

The correct order is: positional parameters, then *args for extra positional arguments, then **kwargs for extra keyword arguments.


True or False#

Question 16

True or False: In Python, a function can return multiple values as a tuple.

Answer

True

Python functions can return multiple values separated by commas, which are automatically packed into a tuple. The caller can unpack them using tuple unpacking.

Question 17

True or False: Default argument values are evaluated once when the function is defined, not each time it is called.

Answer

True

Default values are evaluated once at function definition time. This is why mutable defaults (like lists or dictionaries) can cause unexpected behavior across multiple calls.

Question 18

True or False: The global keyword allows a function to create a new global variable that did not exist before.

Answer

True

The global keyword can both modify an existing global variable and create a new one if it does not already exist in the module scope.

Question 19

True or False: **kwargs collects extra keyword arguments into a list.

Answer

False

**kwargs collects extra keyword arguments into a dictionary, not a list. Each keyword becomes a key, and its argument becomes the corresponding value.

Question 20

True or False: A recursive function without a base case will run forever without error.

Answer

False

Python has a maximum recursion depth (default 1000). A recursive function without a base case will hit this limit and raise a RecursionError, not run forever.

Question 21

True or False: Type hints in Python are enforced at runtime by the interpreter.

Answer

False

Type hints are not enforced at runtime. They are metadata used by developers, IDEs, and static analysis tools like mypy for type checking.

Question 22

True or False: In the LEGB rule, Python checks the local scope before the global scope.

Answer

True

The LEGB order is Local, Enclosing, Global, Built-in. Python checks the local scope first, so a local variable will shadow a global variable with the same name.

Question 23

True or False: Reassigning a parameter inside a function always modifies the original variable outside the function.

Answer

False

Reassigning a parameter inside a function creates a new local binding and does not affect the original variable. Only in-place mutations on mutable objects affect the original.

Question 24

True or False: A Google-style docstring should include a description, an Args section, and a Returns section.

Answer

True

A Google-style docstring includes a one-line description of the function, an Args section listing each parameter and its type/description, and a Returns section describing the return value.

Question 25

True or False: The nonlocal keyword is used to modify a variable in the enclosing function’s scope.

Answer

True

The nonlocal keyword allows a nested (inner) function to modify a variable defined in the enclosing (outer) function’s scope, rather than creating a new local variable.


Essay Questions#

Question 26

Explain the LEGB rule in Python. Describe what each letter stands for and the order in which Python searches for a variable name.

(2-4 sentences)

Answer Guidelines

Key points to include:

  • LEGB stands for Local, Enclosing, Global, Built-in.

  • Local: variables defined inside the current function.

  • Enclosing: variables in the scope of outer functions (for nested functions).

  • Global: variables defined at the module level.

  • Built-in: names in Python’s builtins module (e.g., print, len, int).

  • Python searches in this exact order and uses the first match found.

Question 27

Explain the difference between pass-by-assignment with mutable and immutable objects. Provide an example of each showing different behavior.

(2-4 sentences)

Answer Guidelines

Key points to include:

  • Python passes object references, not values or pointers.

  • Immutable objects (int, str, tuple): modifications inside a function create a new object; the original is unchanged. Example: adding to an integer parameter does not affect the caller’s variable.

  • Mutable objects (list, dict, set): in-place modifications (like append()) affect the original object. Example: appending to a list parameter modifies the caller’s list.

  • Reassignment inside a function always creates a new local binding, regardless of mutability.

Question 28

Explain the mutable default argument problem. Why is def func(items=[]) dangerous, and what is the recommended alternative?

(2-4 sentences)

Answer Guidelines

Key points to include:

  • Default argument values are evaluated once at function definition time, not at each call.

  • If the default is a mutable object (like a list or dict), all calls share the same object.

  • This causes unexpected accumulation of values across calls.

  • The fix is to use None as the default and create a new object inside the function: if items is None: items = [].

Question 29

Describe the two essential components of a recursive function. What happens if one of them is missing?

(2-4 sentences)

Answer Guidelines

Key points to include:

  • A recursive function needs a base case (the stopping condition) and a recursive case (where the function calls itself with a simpler input).

  • Without a base case, the function will recurse indefinitely until Python raises a RecursionError (default depth limit of 1000).

  • Without a recursive case, the function is just a regular function with no recursion.

  • Example: factorial uses n <= 1 as the base case and n * factorial(n - 1) as the recursive case.

Question 30

Explain why type hints are useful even though Python does not enforce them at runtime. Give at least two benefits.

(2-4 sentences)

Answer Guidelines

Key points to include:

  • Type hints improve readability by making function signatures self-documenting.

  • They enable static analysis tools like mypy to catch type errors before runtime.

  • IDEs use them for autocompletion and better code navigation.

  • They make refactoring safer by surfacing type mismatches across the codebase.

  • They serve as documentation that stays in sync with the code (unlike comments that may become outdated).