Ten questions at a time, drawn from 390. Every answer is explained. Nothing is saved and no account is needed.
What is the result of the expression: 10 + 5 * 2?
Practice quiz for Python. Scores are not saved.
What is the result of the expression: 10 + 5 * 2?
Answer: 20. Python follows operator precedence (PEMDAS/BODMAS). Multiplication happens before addition, so 5*2 is 10, plus 10 equals 20. Incorrect options result from processing left-to-right or other order errors.
From lesson: Introduction to Python
Which of the following is true about lists in Python?
Answer: They are ordered collections of items. Lists are mutable and ordered. They can hold mixed data types and are defined using square brackets, not parentheses.
From lesson: Introduction to Python
What happens when you execute 'print(type(5.0))'?
Answer: <class 'float'>. The presence of a decimal point signifies a floating-point number. Integers have no decimal, strings are quoted text, and 'number' is not a base Python type.
From lesson: Introduction to Python
Given the code: x = [1, 2, 3]; y = x; x[0] = 99; what is the value of y[0]?
Answer: 99. In Python, variables for lists are references to the same object in memory. Modifying x affects the object that y also points to. Other answers assume copies are created, which is not the case for simple assignment.
From lesson: Introduction to Python
How do you correctly define a function named 'greet' that takes no arguments?
Answer: def greet():. The 'def' keyword is required, followed by the name, parentheses for arguments, and a colon. Other options use incorrect keywords or omit mandatory parentheses.
From lesson: Introduction to Python
What is the output of print(type(1 / 2))?
Answer: <class 'float'>. In Python 3, the single forward slash operator performs float division, even if both numbers are integers. Options 0 and 2 are incorrect because the result is a float, and option 3 is wrong because this is valid syntax.
From lesson: Variables and Data Types
Which of the following variable assignments will raise a SyntaxError?
Answer: 2nd_value = 20. Variable names cannot start with a number in Python. All other options are valid variable identifiers. Option 0, 2, and 3 are correct naming conventions.
From lesson: Variables and Data Types
If x = '5', what is the result of x + 5?
Answer: TypeError. Python does not allow implicit type conversion between strings and integers during addition. Option 0 and 1 are incorrect because they assume string conversion or integer math occurs automatically. Option 3 is wrong because an exception is raised.
From lesson: Variables and Data Types
Which statement best describes the mutability of a Python tuple?
Answer: Tuples are immutable and cannot be changed after creation.. Tuples are defined as immutable sequences. Once created, their elements cannot be modified, added, or removed. Options 0, 2, and 3 are incorrect because mutability is a property of the data type itself, not dependent on content or size.
From lesson: Variables and Data Types
What is the result of print(bool('False'))?
Answer: True. The bool() function returns True for any non-empty string, regardless of the text contained within it. Therefore, 'False' as a string is truthy. Options 0, 2, and 3 are incorrect because they misunderstand the truthiness of non-empty strings.
From lesson: Variables and Data Types
What is the result of the expression: 10 // 3 + 1?
Answer: 4. Floor division // takes precedence over addition. 10 // 3 is 3, and 3 + 1 is 4. Option 0 is the result of true division, option 1 is the floor result without the addition, and option 3 is a float, which the // operator does not return when applied to integers.
From lesson: Operators (Arithmetic, Comparison, Logical)
What is the output of: 5 > 3 and 2 < 4?
Answer: True. Both comparisons evaluate to True. 'True and True' results in True. Option 1 is incorrect because the condition is met. Options 2 and 3 are incorrect because logical operators return the boolean result of the expression when both operands evaluate to boolean True/False.
From lesson: Operators (Arithmetic, Comparison, Logical)
What is the value of: 10 or 20?
Answer: 10. The 'or' operator returns the first truthy value. Since 10 is non-zero (truthy), it is returned immediately. Options 0 and 1 are incorrect because the operator returns the operand, not a boolean, and option 3 is ignored due to short-circuiting.
From lesson: Operators (Arithmetic, Comparison, Logical)
What is the result of the expression: 5 == 5.0?
Answer: True. Python compares the numeric value of 5 and 5.0, finding them equal. Option 1 is wrong because numeric equality ignores type differences. Options 2 and 3 are wrong because this is a valid arithmetic comparison.
From lesson: Operators (Arithmetic, Comparison, Logical)
Which of the following is equivalent to: not (x == y or x > z)?
Answer: x != y and x <= z. Applying De Morgan's Law, 'not (A or B)' is 'not A and not B'. Thus, 'not (x == y)' is 'x != y' and 'not (x > z)' is 'x <= z'. Option 0 lacks grouping, option 2 uses the wrong logical operator, and option 3 is equivalent but less idiomatic than using direct inequality operators.
From lesson: Operators (Arithmetic, Comparison, Logical)
What is the result of 'Python'.replace('P', 'J')?
Answer: Jython. The correct answer is 'Jython' because .replace() creates a new string with the specified substitutions. Option 2 is wrong because 'P' is not at the end, Option 3 is wrong because strings are immutable but .replace() returns a copy, and Option 4 is wrong because .replace() is a valid method, not an assignment operation.
From lesson: Strings and String Methods
Given s = ' Hello World ', what is the result of s.strip().split(' ')?
Answer: ['Hello', 'World']. The strip() method removes the outer whitespace, resulting in 'Hello World'. Splitting this string by a single space results in a list of two words. Option 1 is wrong because strip removes the outer spaces, Option 3 is wrong because there is only one space between the words, and Option 4 ignores the effect of strip.
From lesson: Strings and String Methods
Which of the following expressions evaluates to True?
Answer: 'a' in 'banana'. Option 3 is true because 'a' exists within 'banana'. Option 1 is false due to case sensitivity. Option 2 is false because uppercase letters have lower Unicode values than lowercase letters. Option 4 is false because 'abc'.upper() returns 'ABC', which is not equal to 'abc'.
From lesson: Strings and String Methods
If s = 'Data', what is the output of s[1:3]?
Answer: 'at'. Slicing in Python is inclusive of the start index and exclusive of the end index. Index 1 is 'a' and index 2 is 't'. Option 1 is wrong as it slices indices 0 and 1. Option 3 includes index 3, and Option 4 includes index 0.
From lesson: Strings and String Methods
What does '123'.isdigit() return?
Answer: True. The .isdigit() method returns True if all characters in the string are digits. Option 2 is wrong because the string contains only numeric characters. Option 3 is wrong because the method returns a boolean, not the string itself, and Option 4 is wrong because the method is valid for strings of digits.
From lesson: Strings and String Methods
If you write age = input('Enter age: '), what is the data type of the variable 'age'?
Answer: str. The input() function always captures user keystrokes as a string. Option 0 and 1 are wrong because conversion requires int() or float(). Option 3 is wrong because input() will always return something, even if it is an empty string.
From lesson: User Input and Output
Which of the following is the most idiomatic way to display the variable 'x' inside a string in modern Python?
Answer: print(f'Value is {x}'). f-strings (Option 1) are the modern, readable standard. Option 0 causes errors if x is not a string. Option 2 adds an unwanted space. Option 3 uses outdated C-style formatting.
From lesson: User Input and Output
What happens if you run print('A', 'B', sep='-')?
Answer: A-B. The 'sep' parameter defines what character sits between the arguments. Option 0 is the default if sep were empty, Option 2 is the default behavior, and Option 3 is incorrect as this is valid syntax.
From lesson: User Input and Output
What is the result of print('Hello', end='!')?
Answer: Prints Hello! without a new line. The 'end' parameter replaces the default newline character. Option 0 describes the default behavior. Option 2 is incorrect syntax-wise. Option 3 misinterprets how the end parameter functions.
From lesson: User Input and Output
How do you correctly read two numbers entered on a single line separated by a space into two separate variables?
Answer: a, b = input().split(). split() breaks a string into a list based on whitespace. Option 1 expects two separate lines. Option 2 attempts to unpack a single string into two variables without splitting, causing an error. Option 3 expects two lines.
From lesson: User Input and Output
What is the result of the expression int(3.9) + int(-3.9)?
Answer: 0. int() truncates towards zero. int(3.9) becomes 3 and int(-3.9) becomes -3. 3 + -3 equals 0. It is not rounding, so -4 or 4 are incorrect.
From lesson: Type Conversion and Casting
Which of the following boolean evaluations is True?
Answer: bool('False'). In Python, non-empty strings are truthy, even if the content is 'False'. 0.0, empty lists, and None are all falsy.
From lesson: Type Conversion and Casting
What will print(int('101', 2)) output?
Answer: 5. The int() function accepts a second argument for base. Converting the binary string '101' to base-10 results in 5 (1*4 + 0*2 + 1*1).
From lesson: Type Conversion and Casting
If x = '10' and y = 5, what is the output of print(x * y)?
Answer: 1010101010. Multiplying a string by an integer performs string repetition. '10' repeated 5 times is '1010101010'. It does not convert the string to an integer.
From lesson: Type Conversion and Casting
Which conversion is guaranteed to never raise a ValueError?
Answer: str(123). Converting any object to a string using str() is always safe. The other options involve parsing malformed numeric strings, which triggers a ValueError.
From lesson: Type Conversion and Casting
Which of the following is the primary purpose of a Python docstring compared to a standard comment?
Answer: To provide documentation that can be accessed programmatically via the __doc__ attribute.. Docstrings are stored in the __doc__ attribute, allowing tools like help() to retrieve documentation at runtime. Using it to comment out code is incorrect (that is a common misuse), the interpreter doesn't 'ignore' docstrings (they are string objects), and Python is an interpreted language, not a compiled one.
From lesson: Comments and Docstrings
Where must a docstring be placed to be correctly recognized as the documentation for a function?
Answer: Immediately after the function signature, indented inside the function body.. Python requires the docstring to be the first statement in the function body to associate it with that function. Placing it before 'def' makes it a string literal unrelated to the function scope, and placing it at the end is not the standard convention. A separate file is not how Python documentation is handled.
From lesson: Comments and Docstrings
When should you use a '#' comment instead of a docstring?
Answer: To explain a complex or 'tricky' segment of code inside a function.. Comments (#) are intended for explaining implementation details, such as tricky logic or algorithm choices. Docstrings are specifically for describing the interface (purpose, arguments, returns) of modules, classes, and functions.
From lesson: Comments and Docstrings
What happens if you use triple-quoted strings inside a function body but not as the first statement?
Answer: It is treated as a regular string literal and does not get assigned to the __doc__ attribute.. Only the first string literal in a function body is assigned to __doc__. Any subsequent string literals, even if triple-quoted, are treated as standard expression statements and are ignored by the interpreter, but they are not 'docstrings' in the functional sense.
From lesson: Comments and Docstrings
Which statement best describes the best practice for writing comments in Python?
Answer: Write comments to explain why the code is written a certain way, rather than what the code is doing.. Explaining the 'why' adds value by providing context that the code cannot express. Commenting every line is noisy and redundant, avoiding all comments ignores cases where context is necessary, and overusing multi-line comments for simple tasks is considered poor style.
From lesson: Comments and Docstrings
Which of the following describes the execution flow if you have an 'if', 'elif', and 'else' chain?
Answer: Only the first block that evaluates to True will execute, and the rest are skipped.. In an if-elif-else chain, Python checks conditions in order; once a True condition is found, its block runs and the entire chain is exited. Option 0 is wrong because 'elif' prevents subsequent checks; option 2 is wrong because 'else' only runs if no prior conditions are met; option 3 is wrong because execution is dependent on condition evaluation.
From lesson: if / elif / else Statements
What is the output of this code snippet: x = 10; if x > 5: print('A'); if x > 8: print('B');
Answer: A and B. Because these are two separate 'if' statements rather than an 'if-elif', both conditions are checked independently. Both 10 > 5 and 10 > 8 are True. Option 0 and 1 are incomplete, and option 3 is incorrect as both conditions are met.
From lesson: if / elif / else Statements
Consider: 'if x = 5:'. What happens when Python tries to execute this?
Answer: It raises a SyntaxError.. The assignment operator '=' is not a valid expression inside an 'if' statement condition. Python expects a boolean expression or a truthy value, and an assignment statement is syntactically invalid here, resulting in a SyntaxError. The others are incorrect because '=' cannot be used for comparison.
From lesson: if / elif / else Statements
If you want to check if a variable 'score' is between 70 and 80 inclusive, which is the idiomatic Python way?
Answer: if 70 <= score <= 80:. Python allows chained comparisons. '70 <= score <= 80' correctly includes both endpoints (inclusive). Option 0 excludes endpoints, option 1 excludes endpoints, and option 3 uses 'or', which would result in True for almost any number, making it logically incorrect.
From lesson: if / elif / else Statements
What happens if all conditions in an if-elif chain are False and there is no 'else' statement?
Answer: The program moves to the next block of code after the chain without executing any branch.. The 'else' block is optional. If no 'if' or 'elif' conditions evaluate to True, the entire structure is skipped, and execution continues with the next line of code. It does not crash or default to another block, nor does it raise a NameError.
From lesson: if / elif / else Statements
What is the output of: for i in range(1, 4): print(i, end='')
Answer: 123. Range(1, 4) starts at 1 and goes up to, but does not include, 4. 1234 includes the endpoint, 0123 includes the start index 0, and Error is incorrect as range is valid.
From lesson: for Loops
If you have 'my_list = [10, 20, 30]', what is the best way to iterate over both indices and values?
Answer: for i, val in enumerate(my_list): print(i, val). Enumerate is the standard Pythonic way to get both index and value. Using range(len()) is valid but considered unpythonic, while the others only provide values, not indices.
From lesson: for Loops
What happens if you use 'break' inside a nested for loop?
Answer: It terminates only the innermost loop where it is called.. A break statement only exits the current block of the nearest enclosing loop. It does not stop the outer loop, terminate the program, or cause a syntax error.
From lesson: for Loops
What is the result of 'for i in 'Py': print(i)'?
Answer: P y. Strings are iterable in Python, so the loop iterates character by character. 'print' defaults to adding a newline, so P and y are printed on separate lines. 'Py' would require 'end='''.
From lesson: for Loops
Which of the following will iterate over the list in reverse order?
Answer: Both the second and third options are correct.. Both 'reversed()' and slicing '[::-1]' work correctly for iteration. '.reverse()' modifies the list in place and returns None, making it invalid for a 'for' loop expression.
From lesson: for Loops
What is the output of this code? x = 0; while x < 3: print(x); x += 1
Answer: 0 1 2. The loop runs while x is less than 3. It prints 0, 1, and 2, then stops when x becomes 3. Option 0 includes 3, which is false; option 2 starts at 1, skipping 0; option 3 is false because x is incremented.
From lesson: while Loops
Which scenario is the primary use case for a while loop in Python?
Answer: Running code until a specific condition changes regardless of iterations. While loops are condition-controlled, making them ideal for indefinite loops. For loops are better for sequences and known iteration counts.
From lesson: while Loops
If you have a while loop with a condition 'while True:', how can you stop the loop?
Answer: Use a break statement inside an if block. A 'while True' loop is infinite unless interrupted by 'break' or returning. Setting the condition to False inside the body doesn't work because the 'while' header only checks the condition at the start of the next cycle.
From lesson: while Loops
What happens if the condition in a while loop is False from the very beginning?
Answer: The code inside the loop is skipped entirely. A while loop checks its condition before entering the body. If the condition is false initially, the body is never reached. It does not error or loop.
From lesson: while Loops
What is the effect of the 'continue' statement inside a while loop?
Answer: It skips the remainder of the current iteration and jumps to the condition check. The 'continue' statement stops the current iteration and jumps back to the loop header to re-evaluate the condition. Option 0 describes 'break', and the others are incorrect logic for 'continue'.
From lesson: while Loops
What is the result of running a 'break' statement inside a nested loop?
Answer: Only the innermost loop containing the 'break' is terminated.. In Python, 'break' exits the loop it is currently in. Option 1 is wrong because only 'sys.exit()' stops the script. Option 3 is wrong because 'break' does not affect outer loops. Option 4 is wrong because 'break' terminates the loop entirely, not just the current iteration.
From lesson: break, continue, and pass
What happens when 'continue' is executed inside a 'for' loop?
Answer: The remaining code inside the loop for the current iteration is skipped, and the loop proceeds to the next item.. The 'continue' statement skips the remainder of the current loop iteration and proceeds to the next iteration. Option 1 describes 'break'. Option 2 describes an input call. Option 4 is false as it is a valid keyword.
From lesson: break, continue, and pass
When is the 'pass' statement most appropriately used in Python?
Answer: To act as a placeholder where syntactically a statement is required but no code is needed.. 'pass' is a null operation; it is used when a statement is required by syntax (like in an empty function or loop). Option 1 describes 'break'. Option 2 describes 'continue'. Option 4 describes 'return'.
From lesson: break, continue, and pass
Consider a 'while' loop with a 'continue' statement. What determines if the loop runs again?
Answer: The loop condition is re-evaluated immediately after the 'continue' statement.. After 'continue', execution jumps back to the top of the loop to re-check the condition. Option 1 is wrong because the condition must still be true. Option 3 describes 'break'. Option 4 is irrelevant to loop control.
From lesson: break, continue, and pass
Which of the following will cause a SyntaxError in Python?
Answer: Using 'break' outside of any loop structure.. A 'break' statement must be inside a loop; if it is outside, Python raises a SyntaxError. Option 1 is correct syntax for placeholders. Option 2 is valid use. Option 3 is valid because the 'if' is inside a loop, so the 'break' context is correct.
From lesson: break, continue, and pass
What is the output of the following code: for i in range(2): for j in range(2): print(i, j)?
Answer: 0 0, 0 1, 1 0, 1 1 on separate lines. The outer loop runs twice (i=0, then i=1). For each i, the inner loop runs twice (j=0, then j=1). This prints 4 pairs. Other options fail to account for the nested iteration cycle.
From lesson: Nested Loops and Patterns
To print a right-angled triangle of stars with 'n' rows, what should the inner loop range be?
Answer: range(i + 1). To get 1 star in row 0, 2 in row 1, etc., the inner loop must iterate 'i+1' times. Using 'range(n)' makes a square; 'range(i)' misses the first star; 'range(1, n)' results in incorrect counts.
From lesson: Nested Loops and Patterns
If you want to create a pattern that prints a 5x5 grid of numbers starting from 1 to 25, which logic is correct?
Answer: print(i * 5 + j + 1, end=' '). i*5 handles the row offset, and j+1 handles the column increment, resulting in a sequential grid. The others result in arithmetic patterns that do not produce a simple 1-25 sequence.
From lesson: Nested Loops and Patterns
How do you prevent a print statement in a nested loop from moving to the next line prematurely?
Answer: Use print(value, end=" "). The 'end' parameter in Python's print function defaults to '\n', so setting it to a space or empty string keeps the cursor on the same line. The other arguments provided are either incorrect or do not control the newline behavior.
From lesson: Nested Loops and Patterns
What happens if you have an empty print() statement inside the outer loop but outside the inner loop?
Answer: It moves to the next line after the inner loop completes its iteration. An empty print() call defaults to printing a newline character. Placing it after the inner loop finishes ensures that each row of the pattern appears on its own line. The other options describe effects that do not align with standard Python print behavior.
From lesson: Nested Loops and Patterns
What is the output of list(range(2, 8, 2))?
Answer: [2, 4, 6]. The correct answer is [2, 4, 6]. range starts at 2, stops before 8, and steps by 2. Option 0 is wrong because it includes 8, which is the exclusive stop limit. Option 2 ignores the step of 2. Option 3 is mathematically incorrect based on the step logic.
From lesson: The range() Function
Which of the following would produce the sequence [10, 8, 6]?
Answer: range(10, 5, -2). range(10, 5, -2) is correct because it starts at 10, moves by -2, and stops before 5 (stopping at 6). Option 0 stops at 8 (not including it). Option 2 stops at 8 (not including it). Option 3 produces [10, 8, 6, 4].
From lesson: The range() Function
What happens if you run print(range(5, 2))?
Answer: It prints range(5, 2). In Python 3, range() returns an object, so printing it directly displays the range signature. Options 0 and 1 are wrong because range returns the object, not a list. Option 3 is wrong because the range is empty (start > stop with default positive step).
From lesson: The range() Function
Why does range(0, 10, 0) raise a ValueError?
Answer: Because a step of zero would result in an infinite loop. The step argument cannot be zero because it would cause an infinite repetition of the same value. Option 0 is incorrect as range() allows ascending sequences. Option 1 is false since 0 can be a start or stop. Option 3 is unrelated to the logic of the function.
From lesson: The range() Function
What is the result of len(range(0, 100, 5))?
Answer: 20. The range contains values 0, 5, 10... up to 95. This is 20 items. 100/5 = 20. Option 1 is the stop value, not the count. Option 2 and 3 are simple counting errors based on inclusive/exclusive off-by-one mistakes.
From lesson: The range() Function
What is the output of the following code? def add(a, b): return a + b result = add(5, 3) print(result)
Answer: 8. The function correctly takes two arguments, adds them, and returns the integer sum. 5 and 3 are incorrect as they are just the inputs; None is incorrect because the return statement exists.
From lesson: Defining and Calling Functions
Which of the following describes why we use parameters in function definitions?
Answer: To allow the function to operate on different data inputs each time it is called.. Parameters provide a way to pass data into a function, making it reusable. Speed is unaffected, parameters do not create global variables, and they do not affect the necessity of return statements.
From lesson: Defining and Calling Functions
Consider this function: def update(x): x = x + 1 return x val = 10 update(val) print(val) What is printed?
Answer: 10. Integers are immutable in Python; the function creates a new local value but does not modify the 'val' variable in the global scope. 11 is wrong because the result was not reassigned; the others are incorrect as no error occurs and it prints the original value.
From lesson: Defining and Calling Functions
What happens if you define a function with a default parameter like 'def greet(name="User")' but call it as 'greet("Alice")'?
Answer: The function uses "Alice" as the name.. Positional arguments passed during a call override the default parameter value. SyntaxError and ignoring the argument are incorrect as the code is valid; "User" is incorrect because the default is only used if no argument is provided.
From lesson: Defining and Calling Functions
What is the result of calling a function that has no 'return' statement?
Answer: It returns None.. In Python, functions implicitly return None if no return statement is executed. The program does not crash, nor does it return 0 or False by default.
From lesson: Defining and Calling Functions
What is the result of calling a function with a mutable default argument, such as 'def add_item(item, list=[]): list.append(item); return list', multiple times?
Answer: The list persists across calls because default arguments are evaluated at definition time.. The correct answer is the third option because default values in Python are created when the function is defined, not when it is called. Option 1 and 2 are incorrect because the list object is shared. Option 4 is incorrect because Python does not prevent using mutable objects as defaults, even if it is a common logic trap.
From lesson: Function Arguments and Parameters
Which of the following describes the behavior of passing an integer to a function and incrementing it inside?
Answer: The integer is immutable, so the function creates a new local integer, leaving the original unchanged.. Integers are immutable in Python, so attempting to change them creates a new local reference; the caller's value remains unchanged. Option 1 is wrong because integers are not passed by reference. Option 3 is wrong because integers are valid arguments. Option 4 is wrong because scope rules do not work that way.
From lesson: Function Arguments and Parameters
What happens if you provide a keyword argument before a positional argument in a function call?
Answer: A SyntaxError is raised because positional arguments must follow the order of the definition.. Python strictly enforces that positional arguments appear before keyword arguments. Option 1 is false because Python does not guess intent. Option 2 is false because it violates syntax rules. Option 4 is false because keyword precedence does not override positional requirement rules.
From lesson: Function Arguments and Parameters
What does the **kwargs syntax allow a function to do?
Answer: Accept an arbitrary number of keyword arguments as a dictionary.. **kwargs captures excess named arguments into a dictionary. Option 1 describes *args. Option 3 is incorrect because type conversion is not the purpose. Option 4 is incorrect because it relates to standard default argument syntax.
From lesson: Function Arguments and Parameters
If a function is defined as 'def func(a, b=2, *c):', which of the following calls is valid?
Answer: func(1, 3, 4, 5). Option 0 is correct because 'a' takes 1, 'b' takes 3, and 'c' captures the remaining positional arguments as a tuple (4, 5). Option 1 and 2 are invalid because positional arguments follow keyword arguments. Option 3 is invalid because 'c' is an *args parameter and cannot be assigned by keyword.
From lesson: Function Arguments and Parameters
What is the output of this code? def append_to(element, to=[]): to.append(element) return to print(append_to(1)) print(append_to(2))
Answer: [1], [1, 2]. The list is created at definition time. Option 1 is wrong because it assumes the list is created per call. Option 2 is correct because the same list is mutated. Option 3 assumes order reversal. Option 4 is wrong because mutable defaults are allowed, just discouraged.
From lesson: Default and Keyword Arguments
Which of the following function definitions is syntactically invalid?
Answer: def func(a=5, b): pass. In Python, mandatory positional arguments cannot follow arguments with default values. Option 1, 2, and 4 are valid because they respect the positional-then-default order.
From lesson: Default and Keyword Arguments
How can you safely use a list as a default argument to ensure a new list is created each time?
Answer: def func(data=None): if data is None: data = []. Option 3 is the standard idiomatic way to handle mutable defaults safely. Option 1 uses a function call at definition, which doesn't solve the state persistence. Option 2 behaves differently if data is an empty list (evaluates to false). Option 4 correctly checks for None.
From lesson: Default and Keyword Arguments
Given `def func(a, b=2):`, which of these is an INCORRECT way to call the function?
Answer: func(b=3, 1). Python requires positional arguments to come before keyword arguments. Option 3 is incorrect because '1' is positional and comes after 'b=3', which is a keyword argument.
From lesson: Default and Keyword Arguments
When are default argument values evaluated in Python?
Answer: Only once, when the function definition is executed. Default arguments are bound at the moment the 'def' statement is executed. Option 1 is the most common misconception. Option 3 is irrelevant. Option 4 describes when they are used, not when they are evaluated.
From lesson: Default and Keyword Arguments
What is the result of calling a function that performs a calculation but has no 'return' statement?
Answer: It returns None. Functions without an explicit return statement return the 'None' object by default. It does not guess the last variable, nor does it error or return a tuple.
From lesson: Return Values and Multiple Returns
Given 'def get_coords(): return 10, 20', what is the data type of the result if you call 'x = get_coords()'?
Answer: tuple. Python automatically packs multiple return values separated by commas into a single tuple. It does not return them as separate integers or a list.
From lesson: Return Values and Multiple Returns
How do you correctly capture two values returned by a function called 'calculate()'?
Answer: a, b = calculate(). Multiple assignment (unpacking) is the standard Python way to assign individual tuple elements returned by a function to separate variables.
From lesson: Return Values and Multiple Returns
What happens if you have code after a 'return' statement inside the same function block?
Answer: It is ignored and never executed. A return statement immediately terminates function execution. Any code following it within the same block is unreachable dead code.
From lesson: Return Values and Multiple Returns
Which of the following is true about returning values in Python?
Answer: You can return multiple objects which are packed into a tuple. Python allows returning multiple items by packing them into a tuple. You are not limited to one object, no type definition is required, and there is no performance-based requirement to prefer lists over tuples for returns.
From lesson: Return Values and Multiple Returns
What is the output of the following code? `nums = [1, 2, 3, 4]; result = list(map(lambda x: x * 2, nums))`
Answer: [2, 4, 6, 8]. The correct answer is `[2, 4, 6, 8]` because the lambda function multiplies each element in `nums` by 2. The other options are incorrect: the first option doesn’t modify the list, the third squares the elements, and the fourth subtracts 1 before multiplying.
From lesson: Lambda Functions
Which of the following is a valid use of a lambda function?
Answer: Sorting a list of tuples by the second element: `sorted(tuples, key=lambda x: x[1])`. The correct answer is sorting a list of tuples by the second element. Lambdas are ideal for simple, one-line operations like this. The other options are incorrect: recursion in lambdas is possible but discouraged, lambdas cannot contain multiple statements, and using lambdas as default arguments can lead to unexpected behavior due to mutable defaults.
From lesson: Lambda Functions
What does the following code output? `pairs = [(1, 'a'), (2, 'b'), (3, 'c')]; print(sorted(pairs, key=lambda pair: pair[1]))`
Answer: [(1, 'a'), (2, 'b'), (3, 'c')]. The correct answer is `[(1, 'a'), (2, 'b'), (3, 'c')]` because the lambda sorts the tuples by the second element (the string), and the strings are already in alphabetical order. The other options are incorrect: the second option reverses the order, the third swaps the tuple elements, and the fourth sorts incorrectly.
From lesson: Lambda Functions
Why might the following code raise an error? `add = lambda x, y: x + y; print(add(5))`
Answer: The lambda is called with too few arguments.. The correct answer is that the lambda is called with too few arguments. The lambda expects two arguments (`x` and `y`), but only one is provided. The other options are incorrect: lambdas can take multiple arguments, default values are allowed but not required, and lambdas can be assigned to variables.
From lesson: Lambda Functions
Which of the following is NOT a good use case for a lambda function?
Answer: To define a function with a docstring: `func = lambda x: "This squares x" or x ** 2`. The correct answer is defining a function with a docstring. Lambdas cannot have docstrings, and the example provided is syntactically incorrect. The other options are valid use cases: lambdas work well for sorting keys, simple operations, and filtering.
From lesson: Lambda Functions
What happens if a recursive function in Python fails to reach a base case?
Answer: A RecursionError is raised once the maximum recursion depth is exceeded. Python limits recursion depth to prevent stack overflows; exceeding it triggers a RecursionError. It does not auto-switch to iterative, nor does it return None, nor does it crash the physical hardware.
From lesson: Recursion
Which of the following describes the purpose of a base case in recursion?
Answer: To provide a stopping condition to prevent infinite calls. The base case provides a direct result for the simplest input, halting further recursion. It is not for initialization, performance tuning, or low-level memory management.
From lesson: Recursion
If a function `find_sum(n)` calculates the sum of integers up to n, why is `return find_sum(n-1)` inside the function incorrect?
Answer: Because it is missing the addition operation and the base case. To return the sum, you must add 'n' to the result of the recursive call (n + find_sum(n-1)) and include a base case (e.g., if n == 0 return 0). The other options describe non-existent requirements or errors.
From lesson: Recursion
Consider a function that traverses a nested list using recursion. Why is it often better than a loop?
Answer: It naturally handles structures of unknown or variable depth. Recursion is ideal for tree-like or nested structures because the stack automatically keeps track of the current depth, which is difficult to manage with simple loops. It does not use less memory, optimize structures, or force the use of sum().
From lesson: Recursion
What is the result of calling a recursive function that returns a value but the intermediate calls do not return the result of the next call?
Answer: The function will return None after the first call finishes. If a recursive call is made but its return value is not captured or returned, the function effectively finishes its execution path without passing the data back up the stack, usually resulting in None. It isn't an infinite loop, nor does it return the correct result.
From lesson: Recursion
What is the output of this code: x = 10; def func(): x = 5; func(); print(x)?
Answer: 10. The output is 10. The 'x' inside the function is a local variable; it does not affect the global 'x'. 5 is incorrect because the local variable is destroyed after the function returns; NameError is incorrect as the global 'x' is valid; None is incorrect as 'x' is defined.
From lesson: Scope, Global, and Local Variables
What happens when you run: x = 10; def func(): print(x); x = 5; func()?
Answer: UnboundLocalError. This raises an UnboundLocalError because Python detects 'x' is assigned in the function, making it local, but it is printed before the assignment occurs. 10 and 5 are incorrect because the function scope is determined before execution; None is incorrect as there is no return statement.
From lesson: Scope, Global, and Local Variables
Which statement correctly describes how Python searches for a variable's value?
Answer: It searches local, then enclosing, then global, then built-in.. Python follows the LEGB (Local, Enclosing, Global, Built-in) rule. The other options are incorrect because they describe the search order incorrectly, which would lead to shadowed variables or scope errors.
From lesson: Scope, Global, and Local Variables
If you define a variable 'y' inside a 'for' loop, where can you access 'y' after the loop finishes?
Answer: Anywhere in the same function or global scope.. Python does not have block-level scope, so variables defined in loops persist in the enclosing function or global scope. Option 1 and 4 are incorrect because they restrict scope falsely, and 2 is incorrect because the variable remains in memory.
From lesson: Scope, Global, and Local Variables
How can you modify a global list variable from within a function without using the 'global' keyword?
Answer: You can use list methods like .append() because they mutate the object.. Mutable objects like lists can be modified in-place without the 'global' keyword because you aren't reassigning the variable name to a new object. The other options are wrong because they suggest impossible restrictions or invalid reassignments.
From lesson: Scope, Global, and Local Variables
What is the result of executing this code: def make_adder(n): return lambda x: x + n; add5 = make_adder(5); print(add5(10))?
Answer: 15. The correct answer is 15. The lambda captures 'n' from the outer scope, creating a closure that adds 5 to the input 10. Other options are mathematically incorrect based on the closure's state.
From lesson: Closures
Why does a standard loop inside a function creating closures often result in all closures using the final value of the loop variable?
Answer: Closures look up the variable name in the outer scope at execution time.. Closures use late binding; they look up the value of the variable in the parent scope when they are finally executed, by which time the loop has finished and the variable holds the last value. This is why the other options are incorrect.
From lesson: Closures
Which keyword is required to modify an integer variable defined in the enclosing function from within a nested function?
Answer: nonlocal. The 'nonlocal' keyword tells Python to search the nearest enclosing scope for the variable rather than creating a local one. 'global' references module level, and the others are not valid keywords for this purpose.
From lesson: Closures
Consider: def outer(): x = 0; def inner(): x += 1; return x; return inner. Why will this raise an UnboundLocalError?
Answer: Because x is being read before assignment inside inner, but assignment makes it local.. When you try to increment 'x', Python interprets 'x' as a local variable for 'inner', but it hasn't been defined yet, hence the error. 'global' is irrelevant here, and integers can be modified with 'nonlocal', not just by assignment.
From lesson: Closures
What happens to the closure's 'environment' (the variables it captures) after the outer function finishes executing?
Answer: It is persisted in a special __closure__ attribute.. Python keeps the captured variables alive in the __closure__ attribute of the function object. It is not destroyed as long as the reference to the inner function exists, it is not copied to global, and it is not immediately collected.
From lesson: Closures
What is the result of executing: x = [1, 2]; y = x; y.append(3); print(x)?
Answer: [1, 2, 3]. In Python, lists are mutable objects passed by reference. Assigning y = x makes y point to the same object as x. Modifying y also modifies x. Option 0 is wrong because the list was modified. Option 2 is wrong because 3 is only appended once. Option 3 is wrong because there is no error.
From lesson: Lists — Creation and Operations
If list_a = [1, 2] and list_b = [3, 4], what does list_a.append(list_b) produce?
Answer: [1, 2, [3, 4]]. The .append() method adds its argument as a single element. Since list_b is a list, it is added as a nested list. Option 0 is the result of .extend(). Option 2 and 3 are incorrect because they change the structure of list_a in ways that .append() does not.
From lesson: Lists — Creation and Operations
Given nums = [10, 20, 30, 40], what is the result of nums[1:3] = [5, 6, 7]?
Answer: [10, 5, 6, 7, 40]. Slice assignment replaces the range defined by the slice (index 1 to 2) with the entirety of the assigned iterable. The indices 1 and 2 (values 20 and 30) are removed, and 5, 6, and 7 are inserted. Option 1 is wrong because it doesn't replace the full slice. Options 2 and 3 are incorrect manipulations of the slice.
From lesson: Lists — Creation and Operations
Which of the following creates a new list containing elements 1, 2, and 3 without modifying the original?
Answer: my_list + [1, 2, 3]. The + operator for lists returns a new concatenated list. .sort(), .append(), and .extend() are all in-place methods that modify the original list and return None, making them unsuitable for creating a new list without side effects.
From lesson: Lists — Creation and Operations
What is the output of: x = [1, 2]; print(x * 2)?
Answer: [1, 2, 1, 2]. When used with a list, the * operator acts as a repetition operator, duplicating the elements of the list. Option 1 is mathematically incorrect for list operations. Option 2 implies an element-wise multiplication which Python lists do not support directly. Option 3 is a misconception of how repetition works.
From lesson: Lists — Creation and Operations
What is the result of executing: x = [1, 2]; y = x.append(3); print(y)
Answer: None. The .append() method performs an in-place modification and returns None. The first option is the state of x, not the return value of the method. The third option ignores the action taken, and there is no error here.
From lesson: List Methods and Slicing
Given my_list = [10, 20, 30, 40], what is the result of my_list[1:3] = [99, 88, 77]?
Answer: [10, 99, 88, 77, 40]. Slice assignment replaces the range [1:3] (indices 1 and 2) with the provided iterable. Since the slice had 2 elements and we provided 3, the list grows in size. Option 1 is correct. Option 2 replaces too much, option 3 is incorrect as this is valid syntax, and option 4 is the right side, not the list.
From lesson: List Methods and Slicing
What is the output of [1, 2, 3].extend([4, 5])?
Answer: None. Similar to .append(), .extend() modifies the list in place and returns None. Option 1 describes the state of the list afterward, not the return value. Option 2 describes what would happen if you used .append().
From lesson: List Methods and Slicing
If a = [1, 2, 3], what happens when you perform a[1:1] = [5]?
Answer: The list becomes [1, 5, 2, 3]. Slicing at index 1:1 specifies an empty slice between index 0 and 1. Assigning a value here inserts it without removing any existing elements. Option 1 correctly reflects an insertion at that position.
From lesson: List Methods and Slicing
Which of the following operations creates a shallow copy of the entire list 'data'?
Answer: All of the above. All three methods (the .copy() method, the full slice operator [:], and the list() constructor) create a new list object with the same elements. Therefore, all are valid ways to create a shallow copy.
From lesson: List Methods and Slicing
What is the output of 'type((5))' in Python?
Answer: <class 'int'>. Parentheses are used for mathematical grouping. Without a comma, (5) is just the integer 5. A tuple requires a trailing comma, as in (5,).
From lesson: Tuples
Which of the following operations will successfully execute on a tuple 't = (1, 2, 3)'?
Answer: new_t = t + (4,). Tuples are immutable, so assignment, append, and remove are invalid. Adding two tuples with '+' creates a new tuple, which is allowed.
From lesson: Tuples
Given 'data = (1, [2, 3])', what happens if you execute 'data[1].append(4)'?
Answer: The list inside the tuple is updated to [2, 3, 4].. The tuple's immutability only prevents you from changing its references. Since the second element is a reference to a mutable list, you can modify the list object itself.
From lesson: Tuples
What is the result of 'a, b = (10, 20, 30)'?
Answer: It raises a ValueError.. Python expects the number of variables to match the number of elements in the tuple. Since there are 3 elements and 2 variables, a ValueError is raised unless a starred expression is used.
From lesson: Tuples
Why would a developer choose a tuple over a list for a collection of data?
Answer: To provide a read-only collection that cannot be accidentally modified.. Tuples are immutable, making them ideal for representing fixed records. Lists are mutable and do not allow being used as dictionary keys, whereas tuples can be used as keys if they contain only immutable items.
From lesson: Tuples
What is the output of len({1, 2, 2, 3, 3, 3})?
Answer: 3. Sets only store unique elements. The duplicates are removed, leaving {1, 2, 3}, which has a length of 3. Other options ignore the property of uniqueness.
From lesson: Sets and Set Operations
Which of the following operations is valid for a set in Python?
Answer: s.add((1, 2)). Sets are unordered, so indexing (s[0]) is impossible. Append is for lists. You can add a tuple because it is immutable (hashable), but you cannot add a list because it is mutable.
From lesson: Sets and Set Operations
What is the result of {1, 2, 3} & {3, 4, 5}?
Answer: {3}. The & operator represents set intersection. It returns elements present in both sets. {3} is the only common element. {1,2,3,4,5} is a union (|), and {1,2,4,5} is a symmetric difference (^).
From lesson: Sets and Set Operations
Which statement best describes the difference between set.remove(x) and set.discard(x)?
Answer: remove() raises a KeyError if x is missing, discard() does not. Both remove elements. The critical difference is error handling; remove() throws an exception if the item is not found, while discard() silently does nothing.
From lesson: Sets and Set Operations
If s = {1, 2, 3}, what happens after s.update([4, 5])?
Answer: s becomes {1, 2, 3, 4, 5}. The update() method takes an iterable and adds all its elements to the set. It merges the contents. The first option would only happen if using add(), but that would fail because a list is not hashable.
From lesson: Sets and Set Operations
Which of the following is a valid way to create a dictionary in Python?
Answer: d = {'a': 1, 'b': 2}. Option 1 creates a set, not a dictionary. Option 2 uses the correct curly brace syntax with key-value pairs. Option 3 creates a tuple of tuples. Option 4 uses square brackets incorrectly. Dictionaries require key:value syntax within braces.
From lesson: Dictionaries
What is the result of calling d.get('x', 0) on a dictionary d that does not contain the key 'x'?
Answer: It returns 0. The get() method returns the provided default value (0) if the key is not found. It does not raise an error, does not return None if a default is provided, and does not modify the dictionary.
From lesson: Dictionaries
Why can you not use a list as a dictionary key?
Answer: Because lists are mutable and therefore unhashable. Dictionary keys must be immutable so that their hash value remains constant. Since lists can be changed (mutated), they are unhashable. The other options are incorrect as lists have length and ordering.
From lesson: Dictionaries
What happens when you use the dictionary unpacking operator **d in a function call?
Answer: It passes the keys and values as keyword arguments. The ** operator unpacks the dictionary into individual keyword arguments, where the key names become the parameter names. It does not pass the dictionary as one object or raise an error.
From lesson: Dictionaries
What is the most efficient way to check if a specific key exists in a dictionary?
Answer: Using 'if key in d:'. Using 'if key in d' is the standard, most idiomatic, and efficient way to check for key existence. 'd.keys()' creates an unnecessary view object, 'try-except' is overhead-heavy, and 'get()' check fails if the key exists but its value is None.
From lesson: Dictionaries
Given data = {'a': 1}, what happens if you execute data.update({'b': 2})?
Answer: data becomes {'a': 1, 'b': 2} and the expression returns None.. The .update() method modifies dictionaries in-place and returns None. The first and second options are wrong because the return value is not the dictionary itself; the fourth is wrong because update accepts any mapping or iterable of key-value pairs.
From lesson: Dictionary Methods and Nested Dicts
Which approach safely retrieves a nested value 'x' inside 'y' from dictionary 'd', returning 0 if either is missing?
Answer: d.get('y', {}).get('x', 0). The first option is safe because it provides a default empty dict if 'y' is missing, then safely checks for 'x'. Option 2 fails if 'y' is missing (AttributeError on None). Option 3 is verbose and prone to errors. Option 4 is risky because if 'x' exists but is 0 or False, the 'or' will return 0 even if the key existed.
From lesson: Dictionary Methods and Nested Dicts
What is the result of {}.fromkeys(['a', 'b'], [])?
Answer: A dictionary where both keys point to the exact same list object in memory.. The value passed to fromkeys is shared by reference among all keys. Option 1 is wrong as it ignores the provided list. Option 2 describes the values but misses the important detail about shared memory. Option 4 is false as values can be any type.
From lesson: Dictionary Methods and Nested Dicts
Why is it generally discouraged to use dictionary unpacking (d1 | d2) to merge nested dictionaries?
Answer: It performs a shallow merge, meaning nested dictionaries in d2 overwrite the reference to nested dictionaries in d1.. The pipe operator creates a shallow copy. If both dictionaries contain a key pointing to a nested dictionary, the second dictionary's nested object replaces the first entirely, rather than merging their contents. The other options are incorrect regarding performance, limitations, or errors.
From lesson: Dictionary Methods and Nested Dicts
What happens when you call pop() on a dictionary with an empty key list?
Answer: It raises a KeyError.. The pop() method requires a key argument to function. Without a key, it raises a TypeError. If you provide a key that doesn't exist, it raises a KeyError. None of the other options describe the standard behavior of the method.
From lesson: Dictionary Methods and Nested Dicts
What is the output of [x**2 for x in range(3) if x % 2 == 0]?
Answer: [0, 4]. The range(3) produces 0, 1, 2. The filter 'if x % 2 == 0' selects 0 and 2. Squaring these yields 0 and 4. Other options fail because they include odd numbers or calculate incorrect squares.
From lesson: List Comprehensions
Which of these correctly implements an if-else mapping in a list comprehension?
Answer: [x if x > 0 else 0 for x in range(5)]. In Python, the ternary operator 'x if condition else y' is placed before the loop. Option 0 has improper syntax, Option 1 uses C-style syntax not valid in Python, and Option 3 applies the logic incorrectly.
From lesson: List Comprehensions
If you need to flatten a 2D list 'matrix = [[1, 2], [3, 4]]', which comprehension is correct?
Answer: [item for row in matrix for item in row]. The order of loops follows nested loop syntax: outer loop first, inner loop second. Option 0 correctly iterates rows then items. Other options fail due to incorrect ordering or structure.
From lesson: List Comprehensions
What happens if you use parentheses instead of square brackets in a comprehension: (x for x in range(3))?
Answer: It creates a generator object.. Parentheses create a generator expression, which yields items one by one lazily. It is not a list, a tuple, or an error. A tuple comprehension does not exist in this syntax.
From lesson: List Comprehensions
What is the result of [x for x in 'ABC' if x == 'B' else 'D']?
Answer: SyntaxError. The syntax is invalid because the 'if-else' must be placed before the 'for' clause when mapping values. You cannot place an 'else' block after a filter condition.
From lesson: List Comprehensions
What is the result of {x: x % 2 for x in range(3)}?
Answer: {0: 0, 1: 1, 2: 0}. The expression creates a dictionary where keys are 0, 1, 2 and values are the remainder of division by 2. Option 1 is incorrect because it is a set literal, not a dict. Option 2 is a partial mapping, and Option 3 calculates the wrong values.
From lesson: Dict and Set Comprehensions
If you define {x**2 for x in [-2, 2]}, what is the resulting object?
Answer: {4}. This is a set comprehension. Since sets only store unique elements, {4, 4} simplifies to {4}. Option 0 is a list, and Option 1 is invalid set representation in output. Option 3 is incorrect because sets ignore duplicates rather than raising an error.
From lesson: Dict and Set Comprehensions
What happens when you run {i: i*2 for i in range(2) for i in range(2)}?
Answer: {0: 2, 1: 2}. The inner loop runs after the outer loop completes, overwriting the keys. The final state of the dict is determined by the last assignment to each key. Option 0 is wrong because it ignores the overwrite behavior. Options 2 and 3 are wrong as the syntax is valid.
From lesson: Dict and Set Comprehensions
Which of the following creates a dictionary mapping each character in 'ABA' to its length?
Answer: {char: len(char) for char in 'ABA'}. Option 0 correctly maps characters to their individual lengths (which is always 1). Option 1 uses list syntax. Option 2 creates a set. Option 3 maps to the total string length, which is not what was requested.
From lesson: Dict and Set Comprehensions
What is the outcome of {x for x in range(5) if x > 2 if x < 4}?
Answer: {3}. The chained 'if' conditions act like a logical AND. Only 3 satisfies both x > 2 and x < 4. Option 1 and 2 include values outside the range, and Option 3 is wrong because 4 is not less than 4.
From lesson: Dict and Set Comprehensions
If you need to implement a queue efficiently in Python, why is collections.deque preferred over a standard list?
Answer: It provides a popleft() method that performs in O(1) time.. deque is implemented as a doubly linked list, making popleft() O(1). Lists require O(n) for shifting elements. The other options are incorrect as memory usage is similar, type enforcement isn't a feature, and list random access is actually faster.
From lesson: Stacks and Queues using Python
What is the result of using a Python list as a stack if you only use .append() and .pop()?
Answer: It behaves as a LIFO structure with O(1) average time complexity.. append() and pop() operate on the end of a list, which are amortized O(1) operations, satisfying LIFO requirements. FIFO is wrong (that's a queue). Memory errors are not standard behavior, and it does not convert to a linked list.
From lesson: Stacks and Queues using Python
Consider a scenario where you must reverse the order of items using a data structure. Which is the most natural fit?
Answer: A Stack, because the last item added is the first one removed.. A stack follows LIFO, which inherently reverses the sequence of elements pushed onto it. Queues maintain order (FIFO). Sets do not preserve order, and dictionaries are for key-value lookups.
From lesson: Stacks and Queues using Python
When implementing a queue using two stacks, what is the process for dequeuing an element?
Answer: If the second stack is empty, pop all elements from the first stack and push them into the second, then pop from the second.. Moving elements from the first to the second stack reverses the order, effectively transforming LIFO into FIFO. Other options are inefficient, use prohibited list operations, or fail to achieve the required order.
From lesson: Stacks and Queues using Python
Why might you choose a list over a deque for a stack implementation in Python?
Answer: Lists are specifically optimized for LIFO operations at the end of the container.. Lists are highly optimized for appending and popping from the end, which is exactly what a stack needs. Deques are better for queues (front/back), but lists are perfectly valid for stacks. The other claims regarding performance speed, thread safety, and boilerplate are either false or misleading.
From lesson: Stacks and Queues using Python
What is the primary purpose of the 'self' parameter in a class method?
Answer: It acts as a reference to the specific instance that called the method.. Option 1 is correct because 'self' allows the method to access and modify the specific attributes of the individual object. Option 0 is wrong because the class itself is accessed via the class name. Option 2 is wrong because arguments are passed after self. Option 3 is wrong because static methods do not use 'self'.
From lesson: Classes and Objects
Given a class 'Car', what happens if you assign a value to a variable directly inside the class body (outside any method)?
Answer: It creates a class attribute shared by all instances of the class.. Option 2 is correct because variables defined directly in the class block are shared across all instances. Option 0 is incorrect because instance attributes must be created using 'self' in a method. Option 1 is incorrect because it is valid syntax. Option 3 is incorrect because the variable persists for the life of the program.
From lesson: Classes and Objects
What is the behavior of the __init__ method?
Answer: It is called automatically when a new instance of a class is created.. Option 0 is correct as __init__ is the constructor method in Python. Option 1 is wrong because Python calls it automatically during instantiation. Option 2 is wrong because it is not static. Option 3 is wrong because the constructor must return None, not a value.
From lesson: Classes and Objects
When inheriting from a parent class, why is super().__init__() typically used?
Answer: To run the parent's initialization logic to ensure proper object setup.. Option 1 is correct; super().__init__() ensures that attributes initialized by the parent are properly assigned. Option 0 is wrong because super does not rename methods. Option 2 is wrong because it does not prevent child attributes. Option 3 is wrong because super has no effect on method staticity.
From lesson: Classes and Objects
What is the result of calling a method on an object if that method was defined without the 'self' argument?
Answer: It raises a TypeError when called on an instance.. Option 2 is correct; Python automatically injects the instance as the first argument, so a method expecting zero arguments will receive one, causing an error. Option 0 and 1 are wrong because the call will always fail. Option 3 is wrong because a method must be decorated with @classmethod to become a class method.
From lesson: Classes and Objects
What is the primary purpose of the __init__ method in a Python class?
Answer: To define the initial state of the object. The correct answer is defining the initial state; it assigns values to attributes. It does not allocate memory (handled by __new__), it doesn't destroy objects (that's __del__), and it does not return the instance (it returns None).
From lesson: The __init__ Constructor
If you define a class without an __init__ method, what happens?
Answer: Python uses a default constructor that does nothing. Python provides a default __init__ that performs no operations, allowing the object to be created successfully. It does not prevent instantiation or raise errors.
From lesson: The __init__ Constructor
Why is the 'self' parameter mandatory in the __init__ method?
Answer: To refer to the specific instance being initialized. Self represents the instance itself, allowing the constructor to assign values to that specific object. It has nothing to do with privacy, speed, or global scopes.
From lesson: The __init__ Constructor
What happens if you explicitly add 'return "Success"' inside an __init__ method?
Answer: A TypeError is raised because constructors must return None. Python strictly forbids return values in the constructor because it is intended only for initialization. Returning a value triggers a TypeError.
From lesson: The __init__ Constructor
Which of the following correctly assigns an attribute to an instance during initialization?
Answer: self.value = value. Using 'self.name = value' attaches the data to the object's dictionary. The other options involve incorrect syntax or assignment order that would not persist the value on the instance.
From lesson: The __init__ Constructor
Given a class 'Data' with a class variable 'count = 0', if you increment it using 'self.count += 1' inside an instance method, what happens?
Answer: It creates a new instance variable 'count' for that specific instance, shadowing the class variable.. Option 2 is correct because the assignment creates a local instance attribute. Option 1 is wrong because assigning to 'self.count' does not update the class attribute. Option 3 is wrong because self can access class attributes. Option 4 is incorrect because attribute lookup is independent of other attributes.
From lesson: Instance vs Class Variables
What is the primary difference between how Python resolves attributes for 'self.var' and 'ClassName.var'?
Answer: self.var checks the instance scope first, while ClassName.var checks the class scope directly.. Option 3 correctly describes the resolution order. Option 1 is wrong because instance lookup happens before class lookup. Option 2 is false as class variables are defined on the class itself. Option 4 is false as the scopes are distinct.
From lesson: Instance vs Class Variables
If a class has a list as a class variable, what is the risk of modifying it via an instance?
Answer: All instances of the class will see the modified list since they share the same object reference.. Option 3 is correct because mutable objects like lists are shared. Option 1 is wrong because the mutation is in-place on the shared object. Option 2 is wrong because Python does not auto-copy. Option 4 is wrong because modifying an existing list is a valid operation.
From lesson: Instance vs Class Variables
Why should you typically initialize variables in the __init__ method?
Answer: To ensure they are unique to every instance created.. Option 2 is correct because __init__ runs per instance creation. Option 1 is wrong because __init__ defines instance variables. Option 3 is wrong as class body definitions are legal. Option 4 is wrong because __init__ does not control visibility.
From lesson: Instance vs Class Variables
When is a variable defined in the class body accessible?
Answer: As soon as the class definition is executed, even without an instance.. Option 3 is correct because class variables are defined when the class is created in memory. Option 1 is wrong because the class exists independently of instances. Option 2 is wrong because they are accessible via the class name. Option 4 is wrong as 'global' has no impact on class attributes.
From lesson: Instance vs Class Variables
What is the primary role of the 'self' parameter in a Python instance method?
Answer: It acts as a reference to the specific object instance the method was called on. Option 2 is correct because 'self' provides access to the attributes and other methods of the instance. Option 1 is wrong because the class is accessed via 'cls'. Option 3 is wrong because inheritance is handled by 'super()'. Option 4 is wrong because arguments are passed as separate parameters following 'self'.
From lesson: Instance Methods
If you have a class 'Robot' with a method 'def greet(self):', how should you invoke this method for an instance 'r1'?
Answer: r1.greet(). Option 3 is correct because the dot notation automatically passes the instance as the first argument. Option 1 is wrong because it lacks the instance context. Option 2 is valid syntax but non-idiomatic, and Option 4 is wrong because 'greet' is not defined in the global scope.
From lesson: Instance Methods
Why does accessing an attribute inside a method require using 'self.'?
Answer: To differentiate between local variables and instance-specific state. Option 1 is correct because local method variables and instance attributes exist in different namespaces. Options 2 and 3 are incorrect regarding Python's scoping rules. Option 4 is wrong; it actually adds a minor lookup step.
From lesson: Instance Methods
What happens if you define an instance method with zero parameters?
Answer: It will raise a TypeError when called on an instance. Option 3 is correct because Python mandates that the instance is passed as the first argument, and a zero-parameter method cannot accept it. Option 1 is false because the passing mechanism occurs regardless. Option 2 and 4 describe behavior related to other method types.
From lesson: Instance Methods
Consider a method defined as 'def update(self, val):'. If you call 'obj.update(10)', what is the value of 'self'?
Answer: The instance 'obj'. Option 4 is correct because 'self' is the placeholder for the instance 'obj'. Option 1 is incorrect because 10 is passed as the 'val' argument. Option 2 confuses the instance with the type. Option 3 is irrelevant to the function's parameter binding.
From lesson: Instance Methods
What is the primary role of the super() function in Python?
Answer: To explicitly call a method from a parent class without knowing its name. Option 0 is correct because super() returns a proxy object that delegates method calls to the parent or sibling classes in the MRO. Option 1 is wrong because it facilitates inheritance, not prevents it. Option 2 is wrong because it delegates to an existing parent, not creating a new object. Option 3 is wrong because super() is about method delegation, not base class instantiation.
From lesson: Inheritance and super()
If you have a class C inheriting from B and A (in that order), what does super() in class C do?
Answer: It calls the next class in the Method Resolution Order (MRO). Option 1 is correct because super() follows the MRO, which is computed based on the class hierarchy. Option 0 is wrong because it calls the *next* class, not necessarily the first parent. Option 2 is wrong because Python does not execute multiple parents in parallel. Option 3 is wrong because super() respects the established hierarchy chain.
From lesson: Inheritance and super()
Why is it dangerous to ignore super().__init__() in a subclass constructor?
Answer: The parent class's instance attributes will not be initialized. Option 1 is correct because parent constructors define essential data fields; missing them causes AttributeErrors later. Option 0 is wrong because omitting super() is syntactically valid code. Option 2 is wrong because Python continues execution until an error is triggered. Option 3 is wrong because subclasses can freely define methods regardless of the parent constructor.
From lesson: Inheritance and super()
In Python 3, how should you correctly call the superclass constructor?
Answer: super().__init__(). Option 2 is the standard Python 3 syntax which automatically detects the class and instance context. Option 0 is invalid syntax for super. Option 1, while technically functional, is the 'old' style and bypasses the MRO logic that super() provides. Option 3 is syntactically incorrect.
From lesson: Inheritance and super()
How does super() handle multiple inheritance scenarios?
Answer: It follows the order defined by the Method Resolution Order (MRO). Option 2 is correct because the MRO provides a linear sequence for delegation. Option 0 is wrong because the MRO is calculated by the C3 linearization algorithm, not just order. Option 1 is wrong because Python is single-threaded in its execution flow here. Option 3 is wrong because super() is built-in and does not require extra decorators.
From lesson: Inheritance and super()
If Class B inherits from Class A, and both define a method 'display()', what happens when you call B().display()?
Answer: The version in Class B overrides the version in Class A.. In Python, methods in a subclass override methods with the same name in the parent class (Method Overriding). Option 0 is wrong because they don't run together automatically. Option 2 is wrong because the subclass method takes precedence. Option 3 is wrong as it is standard behavior.
From lesson: Method Overriding and Polymorphism
What is the primary benefit of using 'super()' in an overridden method?
Answer: It allows the subclass to extend, rather than just replace, the parent's functionality.. super() allows you to trigger the parent's implementation while adding custom logic before or after, thus extending functionality. Option 0 is imprecise. Option 2 is irrelevant. Option 3 is false, as super() is not strictly required for inheritance, only for calling parent code.
From lesson: Method Overriding and Polymorphism
Given the following: class A: def show(self, x=1): pass; class B(A): def show(self, x): pass. How does this affect polymorphism?
Answer: Polymorphism remains intact as long as B can accept the same inputs A expects.. Polymorphism relies on interfaces; if B's 'show' can handle the inputs A's 'show' handles, the program remains robust. Option 0 is false. Option 1 is too strict; changing the signature is acceptable if it respects the contract. Option 3 is incorrect because a TypeError only occurs if the arguments passed don't match the required definition.
From lesson: Method Overriding and Polymorphism
What does the 'Method Resolution Order' (MRO) determine in Python?
Answer: The sequence in which Python searches for a method in a class hierarchy.. MRO defines the order of classes Python searches when looking for a method, especially in multiple inheritance. Options 0, 2, and 3 describe processes unrelated to the MRO's purpose of finding method definitions.
From lesson: Method Overriding and Polymorphism
How does Python handle 'Method Overloading' (different methods with same name but different arguments)?
Answer: It does not support it; the last defined method replaces earlier ones.. Python does not have overloading based on signature; the final definition of a method name in a class scope is the one that stays. Option 0 is false. Option 1 is false. Option 3 is false as abstract classes do not change this core behavior.
From lesson: Method Overriding and Polymorphism
What is the primary technical purpose of name mangling in Python?
Answer: To allow subclasses to define methods without overriding parent methods accidentally. Mangling allows subclasses to define attributes with the same name as parent attributes without conflict. Option 0 is wrong because mangling is not a security feature. Option 2 is wrong because mangling does not prevent modification. Option 3 is wrong because mangling has no impact on execution speed.
From lesson: Encapsulation and name mangling
Given 'class A: def __init__(self): self.__val = 10', what happens when you attempt to access 'a = A(); a.__val'?
Answer: It raises an AttributeError. Because of name mangling, __val is transformed into _A__val. Accessing __val directly raises an AttributeError because the attribute does not exist under that name. Options 0, 1, and 3 are incorrect as the interpreter cannot find the identifier.
From lesson: Encapsulation and name mangling
How can you access a double-underscore prefixed variable from outside the class successfully?
Answer: By accessing the attribute via the mangled name _ClassName__variable. Python reveals the mangled name, allowing manual access via _ClassName__variable. Option 0 fails because the name is mangled at the class level. Option 2 is false as Python does not enforce private scope. Option 3 is a valid architectural pattern but not the way to access the specific mangled attribute directly.
From lesson: Encapsulation and name mangling
When does name mangling occur during the execution of a Python program?
Answer: During the parsing phase when the class is defined. The interpreter mangles names at parse time within the class body. It does not wait for instantiation (Option 0), does not depend on access type (Option 1), and does not depend on dynamic assignment (Option 3).
From lesson: Encapsulation and name mangling
Which of the following is the recommended Pythonic way to indicate that an attribute is intended for internal use?
Answer: Prefixing the name with a single underscore. A single underscore is the PEP 8 convention for 'internal' or 'protected' attributes. Option 0 is reserved for avoiding name collisions in subclasses. Option 2 is for constants. Option 3 is unnecessarily complex for simple encapsulation.
From lesson: Encapsulation and name mangling
If a custom class defines both __str__ and __repr__, which one is called when you type the variable name into the interactive Python shell?
Answer: __repr__. The interactive shell uses the repr() function to display objects, which calls __repr__. __str__ is only used by print() or str(). The other methods serve entirely different purposes.
From lesson: Magic / Dunder Methods
What is the primary purpose of implementing __call__ in a Python class?
Answer: To allow instances of the class to be treated like functions. __call__ allows an instance to be invoked with parentheses, effectively behaving like a callable object. __init__ handles instantiation, while the others relate to iteration or memory management.
From lesson: Magic / Dunder Methods
Why should you typically implement __eq__ when defining a custom class that represents a data entity?
Answer: To define how to compare two instances for equality. By default, objects are compared by memory identity. __eq__ allows you to define logical equality based on state. Sorting requires __lt__, and lists don't require equality.
From lesson: Magic / Dunder Methods
If you implement __getitem__ in your class, what functionality does it provide?
Answer: Enables subscripting access like instance[key]. __getitem__ allows instances to use index or key notation. Iteration requires __iter__, deletion requires __delitem__, and dot notation access is handled by __getattr__ or __getattribute__.
From lesson: Magic / Dunder Methods
When implementing __add__, why should you return a new instance instead of modifying the existing one?
Answer: To support the immutable nature of operators like + in expressions. The + operator is expected to return a new object (like standard math operations), whereas += (via __iadd__) is used for in-place modifications. Returning the same instance violates the expectation that the original object remains unchanged.
From lesson: Magic / Dunder Methods
What is the primary technical mechanism that prevents an Abstract Base Class from being instantiated in Python?
Answer: The presence of one or more methods decorated with @abstractmethod.. Option 2 is correct because the 'abc' module's metaclass prevents instantiation if any abstract methods remain unimplemented. Option 1 does not exist in Python. Option 3 is unnecessary as the metaclass handles this. Option 4 is invalid because all classes have a base object initializer.
From lesson: Abstract Classes and Interfaces
When using 'typing.Protocol', what determines if a class fulfills the interface?
Answer: The presence of methods and attributes matching the Protocol's structure.. Option 3 is correct because Protocols implement structural subtyping (duck typing). Option 1 is false because explicit inheritance is optional. Option 2 is not a requirement. Option 4 is false because Protocols are designed to be decoupled from the implementation.
From lesson: Abstract Classes and Interfaces
Which of the following best describes the intended use of an Abstract Base Class versus a Protocol?
Answer: ABCs define a common implementation, while Protocols define a common structure.. Option 0 is correct: ABCs are often used for shared code/state (nominal), whereas Protocols focus on what an object can do (structural). Option 1 is misleading as both have runtime implications. Option 2 is incorrect as Protocols support inheritance. Option 3 is false as performance differences are negligible.
From lesson: Abstract Classes and Interfaces
What happens if a subclass fails to override an abstract method defined in an ABC?
Answer: The subclass itself becomes abstract and cannot be instantiated.. Option 2 is correct: if a subclass doesn't implement all abstract methods, it inherits the abstract status and thus cannot be instantiated. Option 1 is incorrect because definition is valid. Option 0 is false as no default is provided. Option 3 is incorrect as the program only crashes upon attempting instantiation.
From lesson: Abstract Classes and Interfaces
Why would you choose to use an interface-like structure (like a Protocol) instead of just using normal duck typing without explicit types?
Answer: To enable static type checking tools like Mypy to catch errors early.. Option 1 is correct: interfaces provide a contract for static analysis to verify that objects provide required methods. Option 0 is false. Option 2 is false as Python does not restrict runtime attributes this way. Option 3 is false as the __class__ attribute is inherent to all objects.
From lesson: Abstract Classes and Interfaces
Which decorator argument prevents a dataclass instance from having its attributes modified after initialization?
Answer: frozen=True. frozen=True makes the object hashable and immutable. slots=True optimizes memory, order=True enables comparison operators, and init=False disables automatic generation of the constructor.
From lesson: Dataclasses
How should you correctly define a field that initializes as an empty list for every new instance?
Answer: data: list = field(default_factory=list). default_factory takes a callable to create a fresh object per instance. Using [] or default=[] shares the list across all instances, and list() at the class level is invalid syntax for field defaults.
From lesson: Dataclasses
When should you use the __post_init__ method in a dataclass?
Answer: To perform validation or calculations after the generated __init__ runs. The __post_init__ method is a hook called automatically after __init__. The other options are handled by the decorator arguments or standard class metadata.
From lesson: Dataclasses
If you define a dataclass with two fields, one with a default value and one without, what is the required order?
Answer: Fields without defaults must come before fields with defaults. Python requires non-default arguments to precede arguments with default values, mirroring standard function signature rules. The other options violate this structural requirement.
From lesson: Dataclasses
What is the primary effect of using 'slots=True' in a dataclass decorator?
Answer: It makes the class faster by reducing memory consumption. Slots reduce memory usage by preventing the creation of per-instance __dict__ objects. While it does prevent adding new attributes, that is a side effect, not the primary purpose of the optimization.
From lesson: Dataclasses
What is the primary advantage of using the 'with' statement when opening a file?
Answer: It guarantees the file is properly closed even if an exception occurs.. The 'with' statement implements a context manager that ensures 'file.close()' is called automatically upon exiting the block, even if an error occurs. Option 0 and 1 are incorrect because performance and privileges are not managed by context managers. Option 3 is incorrect because the 'with' statement does not resolve file locking conflicts.
From lesson: Reading and Writing Files
If you want to append content to the end of an existing file without deleting its current contents, which mode should you use?
Answer: a. 'a' stands for append, which places the file pointer at the end of the file. 'r' is read-only. 'w' truncates the file (deletes contents) before writing. 'x' is for exclusive creation and will raise an error if the file already exists.
From lesson: Reading and Writing Files
Which of the following is the most memory-efficient way to process a 5GB text file?
Answer: Use a loop to iterate over the file object directly.. Iterating over the file object ('for line in file') reads only one line into memory at a time, keeping usage minimal. 'readlines()' and 'read()' load everything into memory, which would crash on a 5GB file. Option 3 is unnecessarily complex compared to simple iteration.
From lesson: Reading and Writing Files
What happens if you open a file in 'w' mode that does not exist?
Answer: Python creates a new, empty file.. In both 'w' (write) and 'a' (append) modes, Python automatically creates the file if it does not already exist. 'r' is the only common mode that raises a FileNotFoundError if the file is missing.
From lesson: Reading and Writing Files
When writing to a file, why might a program's output not appear immediately in the file even after the 'write()' method executes?
Answer: The data is buffered in memory to optimize disk I/O operations.. Python (and the OS) buffers writes to minimize expensive disk operations. Data is only flushed to the actual storage when the buffer is full, the file is closed, or flush() is called manually. Options 0, 1, and 3 are factually incorrect regarding Python's I/O behavior.
From lesson: Reading and Writing Files
What is the primary purpose of the __exit__ method in a context manager?
Answer: To handle cleanup tasks like closing files or releasing locks, even if an exception occurs.. The __exit__ method is specifically designed for teardown/cleanup logic. Option 0 and 1 are handled by __init__ and __enter__ respectively. Option 3 is the purpose of __enter__.
From lesson: Context Managers — the with statement
What happens to a file opened with 'with open(...) as f:' if an exception is raised inside the block?
Answer: The file is closed automatically before the exception propagates.. The 'with' statement guarantees that __exit__ is called, which ensures the file is closed regardless of whether the block finishes successfully or raises an exception. The others are incorrect because they imply resource leakage or unsafe handling.
From lesson: Context Managers — the with statement
Which of the following describes the correct behavior when using multiple context managers in a single 'with' statement?
Answer: The context managers are entered left-to-right and exited in the reverse order.. Multiple context managers (e.g., 'with A() as a, B() as b:') follow a LIFO (Last-In-First-Out) order for exiting. Nesting is an alternative, but the comma-separated syntax is a native feature that follows this specific order.
From lesson: Context Managers — the with statement
If you are creating a class to use with the 'with' statement, what must it contain?
Answer: Both __enter__ and __exit__ methods.. The context manager protocol strictly requires __enter__ (to prepare) and __exit__ (to clean up). The other methods listed are unrelated to the context manager protocol.
From lesson: Context Managers — the with statement
What value does the 'as' clause in a 'with' statement receive?
Answer: The return value of the __enter__ method.. The object assigned to the variable after 'as' is whatever the __enter__ method returns. It is often 'self', but can be any object. The other options do not reflect the standard protocol behavior.
From lesson: Context Managers — the with statement
What is the primary purpose of the 'else' block in a try-except structure?
Answer: To execute code only if no exceptions were raised in the try block.. The 'else' block runs only if the code in the 'try' block succeeds without raising an exception. Option 0 describes the 'except' block, option 2 describes the 'finally' block, and option 3 is logically incorrect as 'else' does not handle errors.
From lesson: try / except / else / finally
If an exception is raised in the 'try' block, in what order does the control flow proceed if an 'except', 'else', and 'finally' are all present?
Answer: try -> except -> finally. If an error occurs, the 'else' block is skipped. The 'except' block executes, followed by 'finally'. Option 0 and 1 are wrong because 'else' only executes on success. Option 3 is wrong because 'except' must precede 'finally'.
From lesson: try / except / else / finally
Consider a function that opens a file. If the file processing fails, you want to close the file and then propagate the error. Where should the file.close() call go?
Answer: In a 'finally' block.. The 'finally' block is guaranteed to run regardless of whether an exception occurred or was caught. If you put it in the 'try' or 'else' blocks, it might be skipped if an error occurs. The 'except' block would only run if an error happens, not if the file processes successfully.
From lesson: try / except / else / finally
What happens if a 'return' statement exists in both the 'try' block and the 'finally' block?
Answer: The 'finally' block's return value overrides the 'try' block's value.. When a 'finally' block contains a return statement, it overrides any return statement that was triggered in the 'try' or 'except' blocks. This is a common source of confusion, making option 0 incorrect.
From lesson: try / except / else / finally
Which of the following is the most Pythonic way to handle a division by zero error while ensuring a message is printed?
Answer: Wrap the division in a try-except ZeroDivisionError block.. In Python, it is often preferred to 'Ask for Forgiveness' (EAFP) using specific exception handling. Option 0 (LBYL) is valid but doesn't use the requested keywords. Option 2 is bad practice (catches too much), and option 3 doesn't prevent the crash, as 'finally' does not suppress exceptions.
From lesson: try / except / else / finally
When should you use the 'IndexError' exception rather than 'KeyError'?
Answer: When accessing a sequence element with an out-of-range integer. IndexError is for sequences like lists or strings. KeyError is for dictionaries. TypeError is for wrong types, and FileNotFoundError is for missing files.
From lesson: Common Built-in Exceptions
Which of the following is the most Pythonic way to handle a potential division by zero error?
Answer: Wrap the division in a 'try-except ZeroDivisionError' block. Using try-except follows the EAFP principle. 'If' statements are less robust against concurrent changes. Catching all 'Exception' is too broad, and importing math doesn't prevent runtime errors.
From lesson: Common Built-in Exceptions
What happens if you raise an exception inside a 'try' block that is not caught by any 'except' clause?
Answer: The exception propagates up the call stack until caught or the program terminates. Python bubbles the exception up the stack. 'Finally' always runs even if not caught. The program does not ignore errors, and 'else' only runs if no exception occurred.
From lesson: Common Built-in Exceptions
What is the primary difference between 'ValueError' and 'TypeError'?
Answer: ValueError is for inappropriate argument values, TypeError is for inappropriate object types. ValueError is for when the object type is correct but the value is unacceptable (e.g., int('a')). TypeError is for when an operation is performed on an incompatible type.
From lesson: Common Built-in Exceptions
In a 'try-except-else-finally' construct, when does the 'else' block execute?
Answer: Only when no exception is raised in the 'try' block. The 'else' block runs only if the code in the 'try' block completes without raising an exception. It is not for exceptions; 'finally' is for cleanup.
From lesson: Common Built-in Exceptions
Which of the following is the recommended best practice when defining a custom exception in Python?
Answer: Inherit from the Exception class.. Inheriting from Exception ensures compatibility with standard error handling blocks. Option 1 makes it unusable as an error, option 3 ignores base class initialization, and option 4 is not how Python handles exceptions.
From lesson: Raising Custom Exceptions
What is the primary reason to raise a custom exception instead of a built-in one like ValueError?
Answer: To provide specific, domain-contextual error information to the caller.. Custom exceptions allow callers to distinguish between specific domain-level failures and generic system errors. Options 1 and 4 are false, and option 2 is generally an anti-pattern.
From lesson: Raising Custom Exceptions
When defining a custom exception, why is it important to include 'super().__init__(*args)' inside the custom class's __init__ method?
Answer: It ensures that the exception arguments are correctly stored in the base Exception structure.. The base Exception class manages internal storage for error arguments; failing to call super prevents these from being accessible. The others are incorrect functional claims.
From lesson: Raising Custom Exceptions
How should an application handle a custom exception that it expects to occur during normal operation?
Answer: By catching it with an 'except' block specifically targeting that class.. Targeted exception handling is precise and prevents silent errors. Options 1 and 3 are too broad or rely on non-pythonic logic, and 4 defeats the purpose of the exception system.
From lesson: Raising Custom Exceptions
Which scenario most justifies the creation of a custom exception class?
Answer: You need to signal a specific business-logic failure that built-in exceptions do not clearly represent.. Custom exceptions are for signaling specific, domain-relevant errors. Option 1 describes a variable, option 3 is false as they have similar memory usage, and 4 makes the exception irrelevant.
From lesson: Raising Custom Exceptions
What is the primary effect of the '@' syntax in Python?
Answer: It passes a function as an argument to another function and reassigns the result.. The @ decorator syntax is syntactic sugar for func = decorator(func). Option 0 is wrong because async uses the 'async' keyword. Option 2 describes memoization, not the decorator mechanism itself. Option 3 is incorrect as Python does not compile to machine code via decorators.
From lesson: Decorators
If you define a decorator with arguments, how many layers of nested functions do you typically need?
Answer: Three layers: the decorator factory, the decorator, and the wrapper.. A decorator that takes arguments requires a factory function that returns the decorator, which in turn returns the wrapper. Option 0 and 1 are insufficient for parameterized decorators, and 3 is unnecessary complexity.
From lesson: Decorators
Why is it important to use functools.wraps within a decorator?
Answer: It preserves the original function's docstring and name.. functools.wraps copies the metadata (name, docstring, etc.) from the wrapped function to the wrapper. Option 0 is false as it adds overhead. Option 1 is false as it's not a type-checking tool. Option 3 is false as it does not affect argument passing.
From lesson: Decorators
Consider a wrapper function defined as 'def wrapper(*args, **kwargs):'. Why are *args and **kwargs used?
Answer: To ensure the decorator can accept any number of positional and keyword arguments.. Using *args and **kwargs ensures the decorator is generic and can wrap functions regardless of their signature. Option 1 refers to 'yield'. Option 2 refers to threading or multiprocessing. Option 3 is unrelated to signature handling.
From lesson: Decorators
What happens if a decorator function does not return the inner wrapper function?
Answer: The original function is replaced by None or whatever the decorator returns.. Decorators replace the original function with the return value of the decorator function. If nothing is returned, the variable becomes None. Option 0 is wrong because it returns the object, not the result. Option 2 is wrong as it won't crash until the function is called. Option 3 is a misunderstanding of how decorators bind.
From lesson: Decorators
What happens when a generator function is called in Python?
Answer: A generator object is returned, and the function body is not yet executed.. Calling a generator function returns an iterator object without executing the function body. Option 1 is wrong because execution is lazy. Option 2 is wrong because return ends the generator. Option 4 is wrong because generators do not store all values.
From lesson: Generators and the yield keyword
Which of the following describes the memory behavior of a generator?
Answer: It stores only the current state, generating values on demand.. Generators are lazy, storing only the state required to produce the next item. Option 1 describes a list. Option 3 is wrong because local variables within the generator function can affect memory usage. Option 4 is incorrect as no output buffer is required.
From lesson: Generators and the yield keyword
What is the primary difference between 'yield' and 'yield from'?
Answer: yield emits a single value, while yield from delegates iteration to a sub-iterable.. yield from is a syntax sugar used to delegate to another iterator, pulling all items from it. Option 1 is wrong as both work with iterables. Option 2 is wrong as yield from doesn't terminate the parent. Option 4 is incorrect regarding performance.
From lesson: Generators and the yield keyword
Given 'gen = (x**2 for x in range(3))', what is the result of list(gen) + list(gen)?
Answer: [0, 1, 4]. Generators are exhausted after the first iteration. The first list() call consumes all values; the second call receives an empty iterator. Options 1 is wrong because the generator doesn't reset. Option 3 is wrong because the first list is created successfully.
From lesson: Generators and the yield keyword
How can you pass a value back into a running generator?
Answer: By using the send() method on the generator object.. The send() method allows passing a value into a generator, which then becomes the result of the yield expression. Option 1 is wrong as yield is a statement, not a variable. Option 3 is wrong as next() only takes the generator object.
From lesson: Generators and the yield keyword
What happens when the __next__() method of an iterator is called after the iterator has been exhausted?
Answer: It raises a StopIteration exception. The iterator protocol mandates that StopIteration is raised to signal the end of data. Returning None or restarting would be ambiguous, and returning the last value would violate the protocol.
From lesson: Iterators and the Iterator Protocol
Which of the following describes the relationship between an iterable and an iterator?
Answer: An iterator is an object that implements both __iter__ and __next__. An iterator must return itself from __iter__ to be used in loops, and implement __next__ to return values. The other options are incorrect because iterators can be iterables, and iterables only strictly require __iter__.
From lesson: Iterators and the Iterator Protocol
Consider code that calls iter() on a list. What is the type of the returned object?
Answer: A list_iterator. Calling iter() on a sequence creates a built-in iterator specifically designed for that sequence type. It is not a new list, a generator, or the original object itself.
From lesson: Iterators and the Iterator Protocol
When you use a for-loop to iterate over an object, what is the very first step the Python interpreter performs?
Answer: Calls __iter__() on the object. The loop protocol requires obtaining an iterator first by calling __iter__(). The other options bypass the iterator protocol, which is fundamental to how Python's loops operate.
From lesson: Iterators and the Iterator Protocol
If you define a class with a __next__() method but no __iter__() method, can it be used directly in a for-loop?
Answer: No, because the for-loop expects an __iter__ method. A for-loop always calls iter() on the provided object to get an iterator. Without an __iter__ method, the object is not considered iterable, even if it has a __next__ method.
From lesson: Iterators and the Iterator Protocol
When does the __enter__ method of a class-based context manager execute?
Answer: When the execution flow enters the block defined by the 'with' statement.. __enter__ runs exactly when the 'with' block starts. Option 0 is wrong because __init__ is for setup, not context entering. Option 2 is wrong because __enter__ always runs regardless of exceptions. Option 3 describes the purpose of __exit__.
From lesson: Context Managers (class-based)
What is the result of returning False from the __exit__ method when an exception occurs inside the 'with' block?
Answer: The exception propagates out of the 'with' block normally.. Returning a falsy value (or None) from __exit__ tells Python to re-raise the exception. Option 0 and 1 are incorrect because they describe suppressing the exception (True). Option 3 is incorrect as warnings are unrelated to flow control.
From lesson: Context Managers (class-based)
If you want to use an object in a 'with' statement like 'with MyClass() as obj:', what must __enter__ return?
Answer: The object itself (self).. The value assigned to the variable after 'as' is the return value of __enter__. Returning 'self' is standard practice. Option 1 is invalid syntax, option 2 is incorrect logic, and option 3 would make 'obj' equal to True.
From lesson: Context Managers (class-based)
Why is it important to implement __exit__ even if you don't intend to handle exceptions?
Answer: To ensure cleanup tasks like closing files or sockets happen automatically.. The main purpose of a context manager is resource management (RAII). Option 0 is for pickling, option 2 is unrelated to manual memory management, and option 3 is not the responsibility of the context manager.
From lesson: Context Managers (class-based)
What happens if __enter__ fails (raises an exception) before the 'with' block is entered?
Answer: The __exit__ method is never called.. If __enter__ fails, the code block never runs and the context was never established, so __exit__ is not called. Option 0 and 1 are incorrect because the manager is not fully initialized. Option 3 is impossible if the block never executes.
From lesson: Context Managers (class-based)
What is the primary difference between partial and a lambda function when creating a new callable?
Answer: partial allows for introspection of the function and arguments, which lambdas do not support well. Option 3 is correct because partial objects have .func, .args, and .keywords attributes, making them introspectable. Option 1 is misleading as speed difference is negligible. Option 2 is incorrect as both capture environments. Option 4 is false as partial works with any callable.
From lesson: functools — partial, reduce, lru_cache
You use reduce(lambda x, y: x + y, [1, 2, 3], 10). What is the result?
Answer: 16. Option 2 is correct because the initial value 10 is the starting accumulator; 10+1=11, 11+2=13, 13+3=16. Options 1, 3, and 4 are incorrect because they fail to account for the initial value correctly or assume the operation is invalid.
From lesson: functools — partial, reduce, lru_cache
If you apply lru_cache(maxsize=128) to a recursive function, what happens if the cache fills up?
Answer: The least recently used entries are discarded to make room for new ones. Option 3 is the definition of Least Recently Used behavior. Option 1 is incorrect because it is designed to manage memory. Option 2 is incorrect as it would lose the benefit of the cache. Option 4 is incorrect as it would be inefficient.
From lesson: functools — partial, reduce, lru_cache
When using partial on a function with keyword arguments, how can you override the pre-filled arguments?
Answer: By calling the partial object with new values for those specific keywords. Option 2 is correct because providing the same keyword argument in the final call overrides the pre-filled one. Option 1 is technically describing the same action, but option 2 is the standard phrasing. Option 3 is false, and option 4 is not the intended way to modify calls.
From lesson: functools — partial, reduce, lru_cache
Which of these is the most appropriate use case for reduce?
Answer: Converting a list of numbers into a single aggregate result. Option 2 is the purpose of reduce (folding). Option 1 and 3 are better served by filter and map (or list comprehensions). Option 4 is better served by the sorted function or list.sort().
From lesson: functools — partial, reduce, lru_cache
If you have a Python script that calculates prime numbers across four threads on a quad-core processor, why might it not run faster than a single-threaded version?
Answer: The Global Interpreter Lock allows only one thread to execute Python bytecode at a time.. Option 1 is incorrect because the OS treats all threads fairly. Option 2 is correct because the GIL restricts execution to one thread at a time, preventing CPU-bound tasks from gaining speedup. Option 3 is false as Python threads are wrappers around native OS threads. Option 4 is false; the memory allocation isn't the primary bottleneck for this behavior.
From lesson: Multithreading with threading
What is the primary benefit of using 'threading' in a Python application that performs network requests?
Answer: It allows the program to initiate new requests while waiting for others to finish.. Option 1 is wrong because processing returned data is CPU-bound. Option 2 is wrong because the GIL still exists. Option 3 is correct because network I/O releases the GIL, allowing threads to overlap their waiting times. Option 4 is incorrect as threading does not perform buffer optimization.
From lesson: Multithreading with threading
What happens if you omit the 'join()' method call on a standard thread in a Python script?
Answer: The thread will be automatically terminated by the interpreter as soon as the main thread completes.. Option 1 is false; threads don't require joining to be created. Option 2 is misleading as the process exits, killing non-daemon threads. Option 3 is correct; non-daemon threads are killed when the main thread stops. Option 4 is false; the program will not wait if the thread is not joined.
From lesson: Multithreading with threading
When is it appropriate to use a 'threading.Lock'?
Answer: When multiple threads need to write to a shared dictionary or list simultaneously.. Option 1 is wrong; threads are not required to be locked unless they share mutable data. Option 2 is correct as shared mutable structures are prone to race conditions. Option 3 is wrong because Locks provide mutual exclusion, not strict sequencing. Option 4 is wrong because you cannot interact with or stop the GIL from a standard thread.
From lesson: Multithreading with threading
Which of these best describes a 'Daemon' thread?
Answer: A thread that is killed automatically when the main program finishes.. Option 1 is false; daemon status is about lifecycle, not priority. Option 2 is correct; daemon threads do not block the program from exiting. Option 3 is false; threads can always be interrupted. Option 4 is false; daemon status has nothing to do with computational complexity.
From lesson: Multithreading with threading
Why is the 'if __name__ == "__main__":' guard required when using multiprocessing on Windows?
Answer: It prevents the child process from re-executing the module's initialization code recursively.. The guard is essential because Windows uses the 'spawn' start method, which re-imports the main script in child processes; without the guard, the code would restart and trigger a fork-bomb. The other options are incorrect because the guard does not manage CPU cycles, IPC, or the GIL.
From lesson: Multiprocessing Basics
What is the primary difference between a Thread and a Process in Python?
Answer: Threads share the same memory space, while Processes have their own private memory space.. Processes are isolated with separate memory, whereas threads share memory, making processes safer but more memory-intensive. The GIL applies to the interpreter, affecting threads, and processes are generally preferred for CPU-bound tasks.
From lesson: Multiprocessing Basics
If you need to aggregate results from several worker processes, which object is most appropriate for thread-safe/process-safe communication?
Answer: A multiprocessing.Queue object.. A Queue is specifically designed for process communication and handles the necessary locking internally. Dictionaries, global variables, and local variables are not shared across process boundaries and will not provide synchronized updates.
From lesson: Multiprocessing Basics
When is it appropriate to use the multiprocessing.Pool class instead of manually creating Process objects?
Answer: When you have a large dataset and need to distribute a function across a fixed number of workers.. Pool is designed for distributing a task across a fixed number of workers (e.g., via map or starmap), which is far more efficient than manual management for bulk data. Manual process management is for specific control, not bulk execution.
From lesson: Multiprocessing Basics
Which of the following describes the limitation of using a multiprocessing.Value object to share data?
Answer: It is automatically locked, but you cannot perform complex operations atomically without an additional Lock.. Value objects provide a shared memory location and automatic locking for a single read/write, but operations like 'x += 1' are not atomic, requiring an explicit Lock to prevent race conditions. The other options misstate the functionality or access rules of shared memory.
From lesson: Multiprocessing Basics
What is the result of calling a function defined with 'async def' without the 'await' keyword?
Answer: The function returns a coroutine object without executing its body. Calling an async function returns a coroutine object. It must be awaited, yielded from, or scheduled on the event loop to execute. The other options are incorrect because the code body does not run until scheduled, and the syntax itself is valid.
From lesson: asyncio and async/await
Why does using 'time.sleep()' inside an 'async def' function defeat the purpose of using asyncio?
Answer: It blocks the thread, preventing the event loop from switching to other ready tasks. The event loop is single-threaded. 'time.sleep()' is a blocking operation that stops the entire thread. 'asyncio.sleep()' is the correct non-blocking version. The other options describe behaviors that do not occur.
From lesson: asyncio and async/await
What is the primary difference between 'asyncio.create_task()' and 'await'ing a coroutine directly?
Answer: 'create_task' schedules the coroutine to run on the loop as soon as possible, while 'await' pauses the current function until it finishes. 'create_task' queues the task for the event loop, allowing execution to continue immediately. 'await' pauses the caller until the specific task completes. Neither creates a new OS thread.
From lesson: asyncio and async/await
When using 'asyncio.gather(*tasks)', what happens if one of the tasks raises an unhandled exception?
Answer: The exception is raised to the caller when 'gather' is awaited. 'asyncio.gather' propagates the exception to the caller when the gathered result is awaited. It does not automatically cancel other tasks unless specifically configured, and it certainly does not ignore the error or crash the loop.
From lesson: asyncio and async/await
What is the correct way to run a top-level async function from a synchronous script?
Answer: Use 'asyncio.run(main())'. 'asyncio.run()' is the recommended modern entry point that handles loop creation and cleanup. 'await' is illegal in synchronous code. 'run_forever' is lower-level and not intended for executing a single entry-point function.
From lesson: asyncio and async/await
What is the primary purpose of running mypy on a Python codebase?
Answer: To perform static analysis and identify potential type inconsistencies. Mypy checks for type consistency without running the code. Option 0 and 3 are incorrect because mypy does not affect execution speed or compilation. Option 2 is incorrect as tools like Sphinx handle documentation.
From lesson: Type Hints and mypy
Given 'def process(items: list[str]) -> None:', which of the following is a valid usage that passes type checking?
Answer: process(['a', 'b', 'c']). The type hint expects a list of strings. Option 0 provides integers, 1 provides a string, and 3 provides None, all of which violate the 'list[str]' signature.
From lesson: Type Hints and mypy
If a function argument can be either an 'int' or a 'float', how should it be correctly type-hinted?
Answer: Union[int, float]. Union represents a set of possible types. Option 1 is too broad, 2 is redundant, and 3 is invalid syntax. 4 indicates an integer that might be None, not a float.
From lesson: Type Hints and mypy
What does the 'Optional[int]' type hint imply in Python type checking?
Answer: The value is either an integer or None. Optional[T] is an alias for Union[T, None]. Option 0 is the behavior of 'int', 1 is syntactically irrelevant, and 3 is a misunderstanding of how Python handles integer precision.
From lesson: Type Hints and mypy
When defining a function that returns nothing, why is '-> None' preferred over omitting the return hint?
Answer: It prevents the developer from returning values by mistake. Explicitly hinting '-> None' tells mypy that the function should not return anything. If you omit it, mypy defaults to 'Any', potentially letting unintended return values go unnoticed. Options 0, 2, and 3 are technically incorrect.
From lesson: Type Hints and mypy
Which approach is the most robust way to create a path to a file inside a subdirectory on multiple operating systems?
Answer: Path('folder') / 'file.txt'. Using the / operator with pathlib handles OS-specific separators automatically. The + operator is meant for strings, manual replacements are error-prone, and as_posix() doesn't change how the path is joined.
From lesson: os and pathlib Modules
Why is 'pathlib.Path(__file__).parent' preferred over 'os.getcwd()' when you need to access a file relative to your script?
Answer: It is relative to the script file location, whereas getcwd() is relative to where the terminal is currently positioned.. The script location is fixed regardless of where the user runs the command from, while getcwd() depends on the user's terminal state. Neither option affects speed or race conditions significantly.
From lesson: os and pathlib Modules
What happens if you call 'Path('data/logs').mkdir()' and the directory already exists?
Answer: It raises a FileExistsError.. By default, mkdir() raises a FileExistsError if the target exists. Option 1 would be true if exist_ok=True was passed. Options 2 and 4 are incorrect behaviors for creating a directory.
From lesson: os and pathlib Modules
You want to find all '.jpg' files in a directory recursively. Which method is most efficient?
Answer: Using pathlib.Path.glob('**/*.jpg').. glob('**/*.jpg') is designed specifically for recursive pattern matching. os.walk() and listdir() require significantly more manual boilerplate code to achieve the same result.
From lesson: os and pathlib Modules
Which statement best describes the return value of 'pathlib.Path('file.txt').resolve()'?
Answer: It returns a Path object pointing to the absolute, symlink-resolved path.. resolve() returns an absolute Path object, expanding symlinks and '..' segments. It does not return a string (that would be str(path)), nor a size, nor a boolean.
From lesson: os and pathlib Modules
If you run 'python script.py arg1', what is the value of len(sys.argv)?
Answer: 2. The list sys.argv always contains the script name at index 0 and the arguments following it. Thus, 'script.py' and 'arg1' result in a length of 2. 0 and 1 are too small, while 3 would imply an extra argument was provided.
From lesson: sys Module
Which method should you use to modify the list of directories Python searches for modules during import?
Answer: sys.path.append(). sys.path is a list of strings representing search paths for modules. Modifying it affects imports. sys.modules is a dictionary of loaded modules, sys.argv holds command-line arguments, and sys.flags holds interpreter status; none of these manage import paths.
From lesson: sys Module
What is the primary difference between sys.exit() and simply letting a script finish naturally?
Answer: sys.exit() raises a SystemExit exception that can be intercepted. sys.exit() raises the SystemExit exception, allowing cleanup code in 'finally' blocks to run. It does not delete variables immediately, it is not a cache-clearing function, and there is a significant difference because normal termination happens without an exception.
From lesson: sys Module
How do you direct error messages specifically to the standard error stream instead of standard output?
Answer: Use print(msg, file=sys.stderr). The print function accepts a 'file' argument which defaults to sys.stdout; setting it to sys.stderr redirects the output. sys.stdout.write() writes to standard output, sys.stdin is for input only, and the 'stream' argument does not exist for print.
From lesson: sys Module
What happens if you modify the sys.modules dictionary in a running program?
Answer: Future imports of the modified module will use the new object or fail. sys.modules caches imported modules. Changing it influences subsequent import statements, which is a common (though risky) technique for mocking. It does not force a crash or reboot, and it certainly has an effect on the import system.
From lesson: sys Module
If you have a naive datetime object 'd' and you want to ensure it is treated as UTC, what is the safest practice?
Answer: d.replace(tzinfo=timezone.utc). Using replace attaches the UTC timezone info without changing the wall-clock time. 'astimezone' is for conversion, 'isoformat' is for string output, and 'convert' does not exist in the library.
From lesson: datetime and time Modules
What is the primary difference between a 'timedelta' and a 'date' object?
Answer: Timedelta represents the duration between two dates, while date represents a specific calendar day.. A 'date' object stores a specific year, month, and day. 'timedelta' represents a span or difference, which is the correct definition. The other options either describe them incorrectly or swap their functionality.
From lesson: datetime and time Modules
When measuring the execution time of a code block for performance profiling, why is 'time.perf_counter()' preferred over 'time.time()'?
Answer: It provides higher resolution and is unaffected by system clock updates.. perf_counter is a monotonic clock specifically designed for performance measurement, ensuring stability against system time adjustments. 'time.time()' is wall-clock time, which can drift, and the other options are technically incorrect.
From lesson: datetime and time Modules
What happens when you subtract two datetime objects from each other?
Answer: A timedelta object representing the duration between them.. Subtracting two datetime objects naturally results in a timedelta object. Returning a float or tuple would require manual conversion, and a new datetime object is logically incorrect for a difference operation.
From lesson: datetime and time Modules
Why should you avoid using 'datetime.now()' when dealing with cross-regional server applications?
Answer: It returns a naive object that defaults to the local system time of the server.. The 'now()' method returns a naive object tied to the host's OS time, which causes ambiguity in distributed systems. It is not deprecated, it supports string formatting, and it does not require a database.
From lesson: datetime and time Modules
You need to store a sequence of events where you frequently remove items from the beginning. Which collection is most efficient?
Answer: deque. A deque is designed for O(1) operations at both ends, making it perfect for queues. Lists have O(n) performance for removals from the start, tuples are immutable, and sets do not maintain order or allow duplicates.
From lesson: collections — Counter, defaultdict, deque, namedtuple
What happens when you access a missing key in a defaultdict(list)?
Answer: Creates a new empty list at that key. The list factory function is executed upon accessing a missing key, automatically assigning an empty list to that key. KeyError would occur in a standard dict; None or an empty dict are not the results of a list factory.
From lesson: collections — Counter, defaultdict, deque, namedtuple
Which of the following is true regarding namedtuple instances?
Answer: They are immutable and look like regular tuples with named access. namedtuples are immutable objects that allow access to elements by field name, providing readability without the overhead of a full class. They are not mutable, cannot be modified via dot notation, and are not designed for arbitrary dictionary-style key lookup.
From lesson: collections — Counter, defaultdict, deque, namedtuple
What is the result of 'Counter('aabbc') - Counter('abc')'?
Answer: Counter({'a': 1, 'b': 1}). Counter subtraction keeps only counts greater than zero. 'aabbc' minus 'abc' results in 'a': 1 and 'b': 1. The other options either fail to account for the subtraction logic or include zero/negative counts which are excluded.
From lesson: collections — Counter, defaultdict, deque, namedtuple
You have a list of items and want to count occurrences. Why choose Counter over a standard dict loop?
Answer: Counter provides convenience methods like most_common(). Counter is built specifically for tallying and provides helper methods like most_common() and arithmetic operations. It is not necessarily faster for all cases or more memory-efficient than a dict; it is simply more specialized.
From lesson: collections — Counter, defaultdict, deque, namedtuple
What is the primary benefit of using itertools.chain() over using the + operator to join three large lists?
Answer: It avoids creating a new intermediate list in memory by lazily yielding elements.. chain() returns an iterator that yields elements one by one, whereas + creates a brand new list in memory containing all elements from the inputs. The other options are incorrect because chain does not sort, does not deduplicate, and memory efficiency is its specific design goal, not just raw execution speed.
From lesson: itertools — chain, product, combinations
You have a list of lists: data = [[1, 2], [3, 4]]. Which approach correctly yields all individual integers using itertools?
Answer: itertools.chain(*data). Using the unpacking operator (*) passes the sub-lists as separate arguments to chain(), which is the correct way to link them. chain(data) would treat the outer list as one object and fail to unpack the inner lists. product and combinations perform different mathematical operations entirely.
From lesson: itertools — chain, product, combinations
If you use itertools.product('AB', repeat=2), what is the resulting output sequence?
Answer: ('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B'). product with repeat=2 generates the Cartesian product of the set with itself. This includes all combinations of the items, including self-pairs. Option 1 is combinations, option 3 is permutations, and option 4 is incomplete.
From lesson: itertools — chain, product, combinations
How does itertools.combinations('ABC', 2) behave compared to itertools.permutations('ABC', 2)?
Answer: Combinations treats ('A', 'B') and ('B', 'A') as the same pair, while permutations treats them as distinct.. Combinations represent unordered subsets, meaning order does not matter (A,B is the same as B,A). Permutations represent ordered arrangements, where order is significant. The other options misstate the fundamental mathematical definitions of these functions.
From lesson: itertools — chain, product, combinations
Why might you prefer itertools.chain.from_iterable(list_of_lists) over itertools.chain(*list_of_lists)?
Answer: It is faster because it does not require unpacking the entire list into function arguments.. from_iterable is memory-efficient because it avoids the overhead of unpacking a potentially massive list into function arguments via the * operator. None of the other options are true: it does not handle non-iterables, it does not filter empty items, and it still returns an iterator.
From lesson: itertools — chain, product, combinations
Which of the following describes the behavior of json.dumps() when passed a Python dictionary containing a set?
Answer: It raises a TypeError because sets are not natively JSON serializable.. The json module does not support sets. Option 0 and 2 are incorrect because there is no built-in conversion for sets, and option 3 is incorrect because the library raises an error rather than ignoring data.
From lesson: json — Serialization and Deserialization
When working with a file object, what is the correct approach to write a Python list to a JSON file named 'data.json'?
Answer: json.dump(my_list, open('data.json', 'w')). json.dump() requires a file-like object as the second argument. Option 0 does not exist, option 1 uses the wrong function (dumps is for strings), and option 3 fails because 'data.json' is a string, not an open file stream.
From lesson: json — Serialization and Deserialization
What is the primary difference between json.load() and json.loads()?
Answer: json.load() reads from a file object, while json.loads() parses a JSON-formatted string.. json.load() is designed to read from a file handle (stream), whereas json.loads() (load-string) parses a string. The other options misrepresent the functionality of the two methods.
From lesson: json — Serialization and Deserialization
If you have a Python dictionary with a datetime object, what happens if you try to serialize it using json.dumps()?
Answer: It raises a TypeError because datetime objects are not JSON serializable.. Datetime objects are not part of the basic types supported by the JSON standard, so Python will raise a TypeError. It does not auto-format, auto-convert, or cast to None.
From lesson: json — Serialization and Deserialization
You have a JSON string with a key using single quotes. What happens when you call json.loads() on it?
Answer: It fails with a JSONDecodeError because JSON requires double quotes.. JSON standards strictly mandate double quotes for keys and strings. The json module enforces this strictly, so using single quotes will result in a JSONDecodeError. The other options incorrectly imply that the parser is permissive or auto-correcting.
From lesson: json — Serialization and Deserialization
Which of the following describes the difference between re.search() and re.match() in Python?
Answer: re.search() checks anywhere in the string, while re.match() only checks from the beginning. Option 2 is correct because re.match() anchors the search to the start of the string, whereas re.search() scans the entire string. Option 1 is wrong because findall() returns all occurrences. Option 3 is wrong as both support strings and bytes. Option 4 is incorrect because their behaviors are distinct.
From lesson: re — Regular Expressions
Given the pattern r'a+b', what will re.search(r'a+b', 'aaaab').group() return?
Answer: 'aaaab'. Option 3 is correct because '+' is a greedy quantifier that matches one or more repetitions of 'a'. Options 1, 2, and 4 are wrong because the quantifier consumes all 'a' characters available before the 'b'.
From lesson: re — Regular Expressions
If you want to make a quantifier non-greedy, what character do you append to it?
Answer: ?. Option 3 is correct; appending '?' makes greedy quantifiers (*, +, {n,}) non-greedy. Option 1, 2, and 4 are invalid syntax for changing quantifier behavior in Python regex.
From lesson: re — Regular Expressions
What is the result of using re.findall(r'(\d+)-(\d+)', '123-456 789-012')?
Answer: [('123', '456'), ('789', '012')]. Option 1 is correct because when capture groups are present in findall(), it returns a list of tuples containing the captured groups. Option 2 would be correct if there were no parentheses. Option 3 is wrong because it ignores the tuple structure. Option 4 is wrong because the pattern matches successfully.
From lesson: re — Regular Expressions
Why is it recommended to use r'...' (raw string notation) for regex patterns?
Answer: It prevents Python from interpreting backslashes as escape characters. Option 3 is correct because raw strings treat backslashes literally, which is crucial for regex tokens like \d or \s. Options 1, 2, and 4 are incorrect because raw strings do not impact performance, Unicode handling, or group access.
From lesson: re — Regular Expressions
What is the primary reason for using logger.info('User %s logged in', username) instead of logger.info(f'User {username} logged in')?
Answer: It avoids performing string formatting if the logging level is currently disabled.. The second option is correct because the logging module performs the formatting lazily only if the log record is actually emitted. The other options are incorrect because f-strings are supported in modern Python, timestamps are handled by the Formatter, and str() conversion happens in both cases.
From lesson: logging Module
If you are writing a reusable library, what is the best practice for initializing a logger?
Answer: Use logger = logging.getLogger(__name__).. Using __name__ ensures that the logger follows the package hierarchy, allowing developers to configure it easily. The other options involve configuring global state, which should only be done by the top-level application, not libraries.
From lesson: logging Module
When an exception occurs, why is logger.exception('An error occurred') preferred over logger.error('An error occurred: ' + str(e))?
Answer: It automatically captures and appends the full traceback information.. logger.exception is specifically designed to be used in except blocks to capture the stack trace. The other options are incorrect because speed is negligible here, it does not change the level, and it does not affect other loggers.
From lesson: logging Module
What happens if you do not define a handler for a logger, but the root logger has one defined?
Answer: The log message is sent to the handlers of the parent loggers (propagation).. Loggers propagate messages to their parents by default. The other options are false because the message is successfully handled by the root logger, no exception is raised, and it works for all levels depending on the root's level setting.
From lesson: logging Module
How can you ensure that different parts of your application output logs to different files simultaneously?
Answer: By attaching different FileHandler instances to different named loggers.. Attaching unique handlers to specific loggers allows for fine-grained control over destinations. basicConfig only works once, setting levels doesn't change destinations, and multiple threads on one file requires locking which the logging module handles internally via handlers.
From lesson: logging Module
When defining an argument, why should you use type=int?
Answer: It validates that the input is a valid integer and converts it from a string.. Option 0 is correct because type=int converts the string input to an integer type. Option 1 is wrong because integers are whole numbers. Option 2 is wrong because the terminal always passes data as strings. Option 3 is wrong because argparse handles logic, not terminal input restrictions.
From lesson: argparse — CLI Arguments
What is the primary difference between a positional argument and an optional argument?
Answer: Positional arguments are determined by their order, while optional arguments use flags.. Option 2 is correct because positional arguments rely on their sequence, and flags are used for optional ones. Option 0 is wrong because positional arguments do not use dashes. Option 1 is wrong because optional arguments are, by definition, not required. Option 3 is wrong because there is no add_positional method.
From lesson: argparse — CLI Arguments
How do you create a flag that behaves as a True/False toggle in a script?
Answer: Use action='store_true'.. Option 1 is correct because store_true automatically assigns True if the flag is present and False otherwise. Option 0 is not a valid action. Option 2 is wrong because type=bool does not parse 'False' correctly from CLI strings. Option 3 is wrong because toggles must be optional flags.
From lesson: argparse — CLI Arguments
If you define an argument with '--input' and the user provides the value 'data.txt', how is this accessed in the object returned by parse_args()?
Answer: args.input. Option 0 is correct; argparse strips the dashes and uses the name of the argument as an attribute. Option 1 is wrong because it uses dot notation, not dictionary bracket notation. Option 2 is wrong because that is the value, not the name. Option 3 is wrong because the object is returned directly, not nested.
From lesson: argparse — CLI Arguments
What happens if a user passes a required positional argument that is not defined in the script?
Answer: The parser displays an error message and terminates the program.. Option 2 is correct because argparse includes built-in error handling for missing arguments. Option 0 is wrong because unparsed arguments generally trigger an error. Option 1 is wrong because it is not a syntax error. Option 3 is wrong because missing required arguments do not allow the script to proceed.
From lesson: argparse — CLI Arguments
Given a list 'a = [[1, 2], [3, 4]]' and 'b = a.copy()', what happens if you execute 'b[0][0] = 9'?
Answer: Both a[0][0] and b[0][0] become 9. A shallow copy creates a new outer list but shares the references to the nested lists. Because they point to the same list objects, modifying 'b' affects 'a'. The other options are incorrect because the outer structure is copied, but the inner elements are not.
From lesson: copy — Shallow vs Deep Copy
Which of the following scenarios absolutely requires a deep copy to ensure complete independence?
Answer: Copying a dictionary containing lists as values. Lists are mutable. In a dictionary of lists, a shallow copy duplicates the dictionary, but the nested lists remain shared. Integers, strings, and tuples of integers are immutable, so sharing references is safe and functionally identical to a deep copy.
From lesson: copy — Shallow vs Deep Copy
What is the result of using 'list(original_list)' compared to a shallow copy?
Answer: It results in the exact same shallow copy behavior. Both the list constructor and .copy() create a new list object with references to the original items, making them both shallow copies. It is not a deep copy, nor is it merely a reference.
From lesson: copy — Shallow vs Deep Copy
If you define a custom class 'Data' and use 'copy.deepcopy(instance)', what is required for it to work?
Answer: By default, Python handles custom objects by recursively copying their __dict__. The copy module's deepcopy function is designed to recursively traverse the object's dictionary and copy attributes. The other options are incorrect because no special inheritance is needed and it does not fail by default.
From lesson: copy — Shallow vs Deep Copy
Consider 'x = [1, 2, 3]'. If you perform 'y = x[:]', which statement is true?
Answer: y is a new list object, but contains the same integer references as x. Slicing with [:] creates a new list (a shallow copy). Since integers are immutable in Python, sharing the references is perfectly safe and efficient. Option 0 is false as they are different objects; option 2 is false as it's only shallow; option 3 is false because integers cannot be modified in place.
From lesson: copy — Shallow vs Deep Copy
What is the primary difference between NumPy arrays and standard Python lists when performing arithmetic?
Answer: Arrays perform element-wise arithmetic, whereas lists concatenate or repeat. Arrays perform element-wise arithmetic (e.g., [1,2]*2 results in [2,4]), while list multiplication repeats the list content. The other options are either false or attribute benefits to lists that actually belong to arrays.
From lesson: NumPy — Arrays and Operations
If you slice an array as 'new_arr = arr[0:2]', what happens when you perform 'new_arr[0] = 99'?
Answer: The original 'arr' is updated because the slice is a view. NumPy slices create views, meaning they point to the original memory buffer. Therefore, modifying the view updates the original array. Options 1 and 4 are incorrect because views are mutable; option 2 is incorrect as NumPy objects are generally mutable.
From lesson: NumPy — Arrays and Operations
When computing a dot product of two 2D arrays, why should you prefer the '@' operator over the '*' operator?
Answer: The '@' operator performs matrix multiplication, while '*' performs element-wise multiplication. In NumPy, '@' is specifically implemented for matrix multiplication (dot product), whereas '*' is the standard Hadamard/element-wise product. Option 3 is false as '*' supports broadcasting; option 4 is false as both work with floats.
From lesson: NumPy — Arrays and Operations
What happens if you try to perform 'arr + 5' where 'arr' is a NumPy array?
Answer: The value 5 is added to every individual element in the array. NumPy uses broadcasting to apply scalar operations to every element in the array simultaneously. It does not raise an error, nor does it append or limit the operation to the first element.
From lesson: NumPy — Arrays and Operations
Given an array of shape (3, 4), what is the shape after calling 'arr.sum(axis=0)'?
Answer: (4,). When axis=0 is specified, the sum operation collapses the rows (the first dimension), leaving the number of columns unchanged, resulting in a (4,) array. Other options represent incorrect dimensional reduction.
From lesson: NumPy — Arrays and Operations
Given a DataFrame 'df', what is the result of 'df[['A', 'B']]'?
Answer: A DataFrame containing column A and B. Passing a list of strings to the selection operator returns a DataFrame containing those columns. The first option is wrong because a single column selection returns a Series, but multiple columns return a DataFrame. The third is wrong because list indexing is supported. The fourth is wrong because strings refer to columns, not rows.
From lesson: Pandas — DataFrames and Series
How does Pandas handle missing values during an arithmetic operation like 'Series_A + Series_B'?
Answer: It results in NaN at the corresponding index. Pandas propagates NaN for missing values in arithmetic operations. It doesn't treat them as zero by default, nor does it raise an error or drop rows automatically. You must use fillna() if you want to treat them as zero.
From lesson: Pandas — DataFrames and Series
What is the primary difference between a Series and a DataFrame?
Answer: Series is 1D and DataFrame is 2D. A Series is a one-dimensional array-like object, while a DataFrame is a two-dimensional, tabular data structure. Both can hold any data type and both have indices. Speed depends on the operation, not the data structure type itself.
From lesson: Pandas — DataFrames and Series
What does the 'inplace=True' parameter in many Pandas methods achieve?
Answer: It modifies the original object and returns None. The 'inplace=True' parameter tells Pandas to apply the operation directly to the existing object instead of returning a modified copy. It returns None because the operation is performed in memory on the original object. It does not improve speed or convert data to NumPy arrays.
From lesson: Pandas — DataFrames and Series
When you perform a boolean selection like 'df[df['col'] > 5]', what is actually happening?
Answer: It filters the DataFrame to rows where the condition is True. This is boolean indexing; Pandas evaluates the condition for every row and returns a DataFrame containing only rows where the condition evaluated to True. The second option describes the result of just 'df['col'] > 5', not the whole expression.
From lesson: Pandas — DataFrames and Series
You need to send a dictionary as a JSON body to an API endpoint. Which parameter should you use and why?
Answer: The 'json' parameter, because it automatically serializes the dict and sets the correct Content-Type header.. The 'json' parameter is designed specifically for API payloads. 'data' is for form-encoding, 'params' is for URL query strings, and 'headers' is for metadata, not the body itself.
From lesson: Requests — HTTP Calls
What is the primary benefit of using 'requests.Session()' over making individual calls with 'requests.get()'?
Answer: It allows for connection pooling, reusing underlying TCP connections.. Session objects enable connection pooling. Options 1 and 2 are incorrect because sessions don't affect rate limits or auto-parsing, and option 4 is incorrect because session usage does not change error handling requirements.
From lesson: Requests — HTTP Calls
A request returns a 404 Not Found status. What will the following code do: 'response = requests.get(url); response.raise_for_status()'?
Answer: It will raise an HTTPError exception.. raise_for_status() triggers an exception for 4xx and 5xx responses. It does not return None, print errors, or retry automatically.
From lesson: Requests — HTTP Calls
Which approach is most robust for handling a server that might be temporarily unreachable?
Answer: Using a try-except block catching requests.exceptions.RequestException.. Exceptions are raised during the connection phase, before a status code is even received. 'verify' relates to SSL, and a timeout alone doesn't handle the exception, it only triggers it.
From lesson: Requests — HTTP Calls
If you want to filter a list of products by a category via a GET request, where should the category information be placed?
Answer: In the 'params' dictionary.. GET requests traditionally use URL query strings for filtering, which are handled by the 'params' parameter. 'data' and 'json' are for body payloads, and 'cookies' are for session tracking.
From lesson: Requests — HTTP Calls
If you are using the object-oriented approach with 'fig, ax = plt.subplots()', which command correctly adds a title to the specific plot area?
Answer: ax.set_title('My Title'). ax.set_title() is the correct method for an Axes object. Option 0 modifies the global state, which is discouraged. Option 2 is invalid because Figures do not have a set_title method. Option 3 is a non-existent method.
From lesson: Matplotlib — Basic Plotting
What happens if you provide only one list to the plt.plot() function?
Answer: It treats the list as y-values and uses the indices of the list as x-values.. Matplotlib defaults to using the list indices (0, 1, 2...) as x-values if only one argument is provided. It does not error (0), does not plot against itself (2), and does not pause for input (3).
From lesson: Matplotlib — Basic Plotting
Why is it often preferred to use 'ax.plot()' instead of 'plt.plot()'?
Answer: It allows for better control when managing multiple subplots within a single figure.. Using Axes objects (ax) allows for explicit referencing, which is necessary when working with multiple subplots. Performance (0) and memory (2) are identical, and both approaches handle the same data types (3).
From lesson: Matplotlib — Basic Plotting
If you want to customize the line style, color, and marker in a single call to ax.plot(), what is the most concise way?
Answer: Pass a format string like 'r--o' as an argument.. Matplotlib's format string shorthand (e.g., 'r--o' for red, dashed line, circle markers) is designed for efficiency. Option 0 is verbose, Option 2 is false, and Option 3 is incorrect syntax.
From lesson: Matplotlib — Basic Plotting
When plotting multiple datasets on the same axes, how do you ensure the viewer knows which line is which?
Answer: Use the 'label' argument in ax.plot() and call ax.legend().. Assigning labels to each plot command and then calling the legend method is the standard way to create a reference key. Options 0, 2, and 3 do not provide an actual legend for the data series.
From lesson: Matplotlib — Basic Plotting
When defining a model, what is the primary purpose of the 'Column' type definition?
Answer: To define the data type and constraints for a database field. Option 2 is correct because Column maps Python attributes to database schema definitions. Option 1 is wrong because table creation requires metadata.create_all(). Option 3 is wrong because Column does not enforce runtime type validation. Option 4 is wrong because primary keys must be explicitly marked.
From lesson: SQLAlchemy Basics
What is the result of session.add(my_object) in SQLAlchemy?
Answer: The object is marked as 'pending' and added to the current session's identity map. Option 2 is correct because add() transitions the object into the session state. Option 1 is wrong because commit() is required for persistence. Option 3 is wrong because validation usually happens upon flush or commit. Option 4 is wrong because SQLAlchemy follows an explicit commit pattern.
From lesson: SQLAlchemy Basics
Why would you choose to use 'joinedload' in a query?
Answer: To improve performance by reducing the number of queries for related objects. Option 1 is correct as it solves the 'N+1' query problem. Option 2 is wrong because joinedload only affects the SELECT statement result. Option 3 is wrong because that is handled by the relationship() function. Option 4 is wrong because that is handled by the cascade setting.
From lesson: SQLAlchemy Basics
What happens if you execute session.rollback()?
Answer: It reverts the session to its state before the current transaction started. Option 2 is correct because rollback undoes uncommitted changes. Option 1 is wrong because it only affects the transaction, not existing table data. Option 3 is wrong because objects remain in memory but their session state resets. Option 4 is wrong because it does not disconnect the engine.
From lesson: SQLAlchemy Basics
How does the 'relationship' function differ from a 'ForeignKey'?
Answer: ForeignKey handles the database structure, relationship handles Python object navigation. Option 2 is correct because ForeignKeys are for database schema linking, while relationship() enables high-level ORM navigation. Option 1 is wrong because both are needed for a full ORM experience. Option 3 is wrong because they serve different layers. Option 4 is wrong because type definitions are not based on the function name.
From lesson: SQLAlchemy Basics
Why does FastAPI recommend using Pydantic models for request bodies instead of raw dictionaries?
Answer: It provides automatic data validation and serialization. Pydantic provides automatic schema enforcement and error messages; raw dictionaries offer no structure, while the other options are either false or unrelated to the primary benefit of Pydantic.
From lesson: FastAPI Quick Overview
What is the primary difference between defining a route with 'def' versus 'async def' in FastAPI?
Answer: FastAPI runs 'def' routes in a separate thread pool and 'async def' on the event loop. FastAPI handles 'def' routes in an external thread pool to prevent blocking the main event loop, whereas 'async def' routes run directly on the event loop. The other options are incorrect interpretations of the architecture.
From lesson: FastAPI Quick Overview
If you define a path operation as '/items/{id}' and another as '/items/recent', why might the latter be unreachable?
Answer: The dynamic route matches 'recent' as an ID before the specific route is reached. Because FastAPI evaluates routes in order, the path parameter '{id}' matches the string 'recent'. The other options are false; FastAPI handles specific and dynamic routes fine if ordered correctly.
From lesson: FastAPI Quick Overview
When declaring a path parameter like 'item_id: int', what happens if a user sends '/items/abc'?
Answer: FastAPI returns a 422 Unprocessable Entity error. FastAPI automatically validates that the input matches the type hint; if it fails to cast to an int, it responds with an automated 422 error. The other options suggest manual handling that FastAPI automates.
From lesson: FastAPI Quick Overview
How does FastAPI determine whether a parameter is a query parameter or a path parameter?
Answer: Parameters matching a path string are path parameters, others are query parameters. If a function argument matches a variable in the path string, it is treated as a path parameter; if not, it is assumed to be a query parameter. The other options do not describe FastAPI's path-matching logic.
From lesson: FastAPI Quick Overview
What is the result of the following code? def func(a=[]): a.append(1); return a; print(func()); print(func())
Answer: [1], [1, 1]. Because the default argument 'a' is initialized when the function is defined, the same list object is reused in subsequent calls. Option 0 and 2 are incorrect because the list is modified; option 3 is incorrect because lists are allowed as default arguments, though it is considered bad practice.
From lesson: Python Interview Questions — Basics
Which of the following correctly describes how Python handles variable scope when using the 'global' keyword?
Answer: It allows a function to modify a variable defined at the module level.. The 'global' keyword allows a function to perform assignments to a variable defined in the module-level scope. Option 0 is wrong as it doesn't cross module boundaries; option 2 is wrong because 'global' doesn't enforce constants; option 3 describes 'nonlocal'.
From lesson: Python Interview Questions — Basics
What is the difference between shallow copy and deep copy in the context of the copy module?
Answer: Both 1 and 2 are correct.. A shallow copy creates a new container but stores references to the original child objects, while a deep copy recursively copies everything. Option 0 is false as copy speed depends on complexity; option 3 correctly identifies both fundamental definitions.
From lesson: Python Interview Questions — Basics
What is the primary behavior of the 'is' operator in Python?
Answer: It checks if two variables refer to the exact same object in memory.. 'is' checks for identity, meaning the objects share the same memory address. '==' checks for value equality. Option 2 describes 'isinstance()', and option 3 is unrelated.
From lesson: Python Interview Questions — Basics
If you have a list 'nums = [1, 2, 3]', what happens if you run 'for x in nums: nums.remove(x)'?
Answer: It removes only some items because the index pointer increments while the list shrinks.. Modifying a list during iteration causes the loop to skip over elements because the indices shift. Option 0 is false because not all elements are processed; option 1 is false because the code is syntactically valid; option 3 is false as the loop will eventually exhaust.
From lesson: Python Interview Questions — Basics
What is the primary difference in performance between checking membership in a list vs. a set?
Answer: Sets use hashing to achieve O(1) average lookup, whereas lists use O(n) linear search.. Sets are implemented as hash tables, making membership lookups O(1) on average. Lists must scan elements one by one, resulting in O(n). Order does not make list lookups faster, and contiguous memory helps iteration but not random lookup performance.
From lesson: Python Interview Questions — Data Structures
Given 'a = [1, 2, 3]' and 'b = a', what happens if you execute 'b.append(4)'?
Answer: Both a and b are updated to [1, 2, 3, 4].. In Python, assigning a list to a new variable name creates a reference to the same object in memory, not a copy. Modifying the list through either name reflects the change globally for both variables. The other options ignore how object references behave.
From lesson: Python Interview Questions — Data Structures
What happens if you use a list as a dictionary key?
Answer: It raises a TypeError because lists are unhashable.. Dictionary keys must be hashable, meaning they must have a stable hash value. Since lists are mutable, their contents can change, making them unhashable. The other options are incorrect because Python does not automatically mutate types to satisfy keys, and even an empty list is mutable.
From lesson: Python Interview Questions — Data Structures
Which of the following best describes the behavior of a Python tuple containing a list?
Answer: The tuple reference is immutable, but the contained list can be modified.. A tuple is a container of references. While you cannot reassign the index of the tuple to point to a different object, you can modify the objects themselves if they are mutable. The other options misinterpret how object references and tuple immutability work.
From lesson: Python Interview Questions — Data Structures
Why is it inefficient to concatenate strings using a loop and the '+' operator?
Answer: Strings are immutable, so every concatenation creates an entirely new string object.. Because strings are immutable, every '+' creates a new string object in memory, leading to O(n^2) complexity. Using ''.join() is efficient because it calculates the total size first. The other options are either false or do not explain the primary performance issue.
From lesson: Python Interview Questions — Data Structures
What happens when you define a method with a double underscore prefix (e.g., __method) in a Python class?
Answer: The interpreter performs name mangling, renaming the attribute to _ClassName__method.. Option 1 is wrong because it can still be accessed via mangled names. Option 2 is correct as it describes how Python avoids collisions in subclasses. Option 3 is wrong because it has no effect on static status. Option 4 is wrong because Python does not enforce strict privacy.
From lesson: Python Interview Questions — OOP
In Python's Method Resolution Order (MRO), how does the language determine which method to call in a multiple inheritance scenario?
Answer: It uses the C3 Linearization algorithm to provide a consistent and predictable method ordering.. Option 1 describes the old-style class behavior. Option 2 is correct as C3 Linearization is the standard used for reliable MRO. Option 3 is wrong because MRO is fixed at class definition. Option 4 is wrong as it doesn't describe the order of inheritance.
From lesson: Python Interview Questions — OOP
When defining a class method, what is the primary purpose of the '@classmethod' decorator?
Answer: It changes the first argument from 'self' to 'cls', allowing access to class state.. Option 1 is wrong because instance variables require an instance. Option 2 is correct because it passes the class itself. Option 3 is wrong as decorators don't restrict overriding. Option 4 is wrong as it is a architectural choice, not a performance optimization.
From lesson: Python Interview Questions — OOP
What is the primary difference between '__init__' and '__new__' in Python?
Answer: __new__ is the constructor responsible for creating the instance, while __init__ initializes it.. Option 1 is backwards. Option 2 is incorrect. Option 3 is correct: __new__ creates the object, and __init__ sets the initial state. Option 4 is wrong because __new__ MUST return an instance, whereas __init__ returns None.
From lesson: Python Interview Questions — OOP
Why is it considered better practice to use properties (@property) rather than public attributes in Python?
Answer: Properties allow for the implementation of getters and setters without changing the external API.. Option 1 is wrong; properties don't provide thread safety. Option 2 is correct; they allow adding logic to attribute access while keeping usage as simple as 'obj.x'. Option 3 is wrong. Option 4 is wrong because there are other ways to restrict access, though properties are the standard way.
From lesson: Python Interview Questions — OOP
What is the primary difference between a shallow copy and a deep copy in the context of the copy module?
Answer: A shallow copy creates a new object and inserts references to the original's child objects; a deep copy creates a new object and recursively copies all objects found in the original.. Option 0 accurately describes the memory management of both techniques. The others are incorrect because they mischaracterize the fundamental nature of references versus recursive cloning.
From lesson: Python Interview Questions — Advanced
When using a decorator that accepts arguments, why is a three-level nested function structure necessary?
Answer: The outermost function accepts the decorator arguments, the second receives the function to be decorated, and the third executes the wrapper logic.. Option 1 correctly identifies the scope requirements of the outer, middle, and inner functions. Other options are irrelevant as they don't explain the scoping necessity for passing arguments to a decorator.
From lesson: Python Interview Questions — Advanced
What happens when you call a generator's .close() method?
Answer: The generator raises a GeneratorExit exception internally at the point where it is currently suspended.. Calling .close() triggers a GeneratorExit exception, allowing the generator to run its 'finally' blocks for cleanup. Other options incorrectly describe resource management or execution flow.
From lesson: Python Interview Questions — Advanced
In the context of Python descriptors, what is the significance of the __set_name__ method?
Answer: It enables the descriptor to know the name of the attribute it is assigned to in the owner class.. __set_name__ was introduced to allow descriptors to know their own attribute name on the class, avoiding hardcoding strings. Other options misidentify its purpose as storage, access restriction, or formatting.
From lesson: Python Interview Questions — Advanced
How does the 'super()' function resolve method calls in multiple inheritance?
Answer: It traverses the Method Resolution Order (MRO) list using the C3 linearization algorithm.. Python uses C3 linearization to create a deterministic MRO, which super() follows. The other options suggest non-existent or incorrect mechanisms for method resolution.
From lesson: Python Interview Questions — Advanced
When converting a nested loop approach (O(n*k)) for a fixed-size subarray sum into a Sliding Window approach (O(n)), what is the fundamental operation that enables the performance gain?
Answer: Subtracting the outgoing element and adding the incoming element to the previous sum.. Option 1 is correct because it maintains a running sum, allowing O(1) updates per step. Option 0 is inefficient for summation. Option 2 increases complexity to O(n log n). Option 3 introduces unnecessary overhead and potential stack overflow.
From lesson: Python Coding Patterns — Sliding Window
In a variable-size sliding window problem where we find the smallest subarray with a sum greater than S, when should the 'left' pointer be incremented?
Answer: Repeatedly until the window sum is no longer greater than S.. Option 2 is correct because the goal is to shrink the window as much as possible while maintaining the constraint. Option 0 would result in a fixed-size window. Option 1 is incorrect as we need the sum to be greater than S. Option 3 is a terminal condition, not a logic step.
From lesson: Python Coding Patterns — Sliding Window
If you are using a sliding window to find the longest substring with at most K distinct characters, why is a dictionary (hash map) an effective data structure to include?
Answer: It tracks the frequency of characters currently in the window to monitor the 'distinct' count.. Option 2 is correct because the dictionary tracks the count of each character, letting us know when we hit K distinct characters. Option 0 is better handled by lists or queues. Option 1 doesn't help with counting frequencies. Option 3 is false as dictionaries are unordered in older Python and insertion order in newer, but they do not sort by character value.
From lesson: Python Coding Patterns — Sliding Window
What is the primary constraint that differentiates a 'Fixed' sliding window from a 'Dynamic' sliding window?
Answer: Fixed windows have a constant length k; dynamic windows change length based on a condition.. Option 1 is the definition of the patterns. Both types use two pointers. Hash maps and lists can be used in either. Both patterns are used for searching and processing, not sorting.
From lesson: Python Coding Patterns — Sliding Window
In a sliding window algorithm, what represents the most efficient way to maintain the window state as you move from index 'i' to 'i+1'?
Answer: Updating the state using only the element entering and the element leaving the window.. Option 2 is the core optimization of the sliding window, keeping complexity linear. Option 0 turns the algorithm into O(n*k). Option 1 is inefficient. Option 3 resets the progress, defeating the purpose of sliding.
From lesson: Python Coding Patterns — Sliding Window
In a sorted array, if the sum of elements at the left and right pointers is less than the target value, what should you do?
Answer: Increment the left pointer. Incrementing the left pointer increases the total sum because the array is sorted. Decrementing the right pointer would decrease the sum, making it further from the target. Moving both or decrementing the right is illogical in a sorted search.
From lesson: Python Coding Patterns — Two Pointers
Why is the Two Pointer pattern generally more efficient than a nested loop approach for finding a pair sum?
Answer: It reduces the time complexity from O(n^2) to O(n). Two pointers allow us to scan the array in a single pass after sorting, resulting in linear time complexity. Nested loops are quadratic O(n^2). While it does use O(1) space, that is not the primary reason for its efficiency relative to nested loops.
From lesson: Python Coding Patterns — Two Pointers
When using two pointers to remove duplicates from a sorted array in-place, what is the role of the 'slow' pointer?
Answer: It marks the position of the last unique element found. The slow pointer maintains the boundary of the 'processed' unique list. The fast pointer searches for new values. Option B describes the fast pointer, while C and D are not standard functions of the slow pointer in this specific pattern.
From lesson: Python Coding Patterns — Two Pointers
What is the primary condition required to use the bidirectional two-pointer approach for finding pairs?
Answer: The list must be sorted in ascending order. The bidirectional approach relies on the property that moving a pointer inward predictably changes the sum (increasing from left, decreasing from right). This only holds if the list is sorted. Positive integers, dictionaries, and even lengths are not requirements.
From lesson: Python Coding Patterns — Two Pointers
If you are asked to 'square a sorted array' and return the result in sorted order, why is two-pointer the best approach?
Answer: It avoids the O(n log n) cost of sorting the squared values. Squaring a sorted list (e.g., [-4, -1, 0, 3, 10]) results in values where the largest numbers are at the ends. Two pointers allow you to pick the largest squares from the ends and place them into a new array backwards in O(n) time, avoiding a full re-sort.
From lesson: Python Coding Patterns — Two Pointers
What is the primary purpose of the 'unchoose' step in a backtracking algorithm?
Answer: To reset the state to allow other branches to explore from a clean slate.. The 'unchoose' step ensures that modifications made to the state during exploration are reversed, which is required for correct backtracking to other branches. Option 0 is the base case's job, 2 is unrelated to state cleanup, and 3 is incorrect because we typically keep track of the result separately.
From lesson: Python Coding Patterns — Recursion and Backtracking
Consider a recursive function calculating factorials. What happens if the recursive step 'return n * factorial(n)' is used instead of 'return n * factorial(n - 1)'?
Answer: It will result in a RecursionError due to exceeding the maximum depth.. Because n never decreases to reach the base case (0 or 1), the function calls itself with the same value infinitely until the stack overflows. Options 0, 1, and 3 are incorrect because they ignore the stack overflow behavior.
From lesson: Python Coding Patterns — Recursion and Backtracking
Which of these is the most effective way to handle overlapping sub-problems in a recursive function?
Answer: Applying memoization to cache results of expensive calls.. Memoization caches results of sub-problems, preventing redundant computations and drastically reducing complexity. Option 0 doesn't fix performance, 1 is bad practice/unsafe, and 3 does not address the efficiency of redundant calls.
From lesson: Python Coding Patterns — Recursion and Backtracking
In Python, what is the default behavior when a recursive function reaches the maximum allowed depth?
Answer: The program raises a RecursionError.. Python has a built-in recursion limit to prevent stack overflows and explicitly raises a RecursionError when this limit is hit. Options 0, 1, and 3 describe behaviors that do not happen in standard Python.
From lesson: Python Coding Patterns — Recursion and Backtracking
When implementing a backtracking algorithm to find all permutations of a list, why is copying the current path before appending it to the result list necessary?
Answer: To prevent subsequent modifications in the backtracking process from changing the stored results.. Because lists are mutable, if you append the reference of the current path, all stored results will reflect the final state of that list. Creating a copy preserves the state at that moment. The other options are incorrect as they do not address the mutability issue.
From lesson: Python Coding Patterns — Recursion and Backtracking
What is the time complexity of checking if an element exists in a Python set containing n elements?
Answer: O(1) on average. Sets are implemented as hash tables, allowing O(1) average lookup. O(log n) is typical for trees, O(n) is for lists, and O(n log n) is for efficient sorting algorithms.
From lesson: Big-O Complexity in Python
Given a list 'data' of length n, what is the complexity of executing 'data.pop(n-1)'?
Answer: O(1). Removing the last element of a list does not require re-indexing, making it an O(1) operation. O(n) is for removing from the front, and the others do not apply to simple list removal.
From lesson: Big-O Complexity in Python
If you iterate through a nested list (a list of lists) where the outer list has length N and each inner list has length M, what is the time complexity?
Answer: O(N * M). A nested loop covering every element in a structure of N lists with M elements each results in N*M iterations. The other options either undercount or misapply complexity rules.
From lesson: Big-O Complexity in Python
Why is using ''.join(list_of_strings) preferred over repeatedly adding strings using '+' in a loop?
Answer: It is O(n) compared to the O(n^2) of repeated concatenation.. Repeated string concatenation is O(n^2) because each step creates a new string copy. join() pre-calculates the size and performs one copy, resulting in O(n). It does not sort or save global RAM by definition.
From lesson: Big-O Complexity in Python
Which of the following operations on a Python dictionary is expected to take O(n) time, where n is the number of keys?
Answer: list(dict.values()). Extracting all values into a list requires visiting every entry in the dictionary, which is O(n). The other three operations are O(1) average time because they rely on hash lookups.
From lesson: Big-O Complexity in Python