Courseiva

CCNA Object-Oriented Programming Questions

34 of 109 questions · Page 2/2 · Object-Oriented Programming · Answers revealed

76
MCQhard

A developer writes a class `Logger` with a method `log(self, message)`. They want to ensure that when `Logger` is subclassed, any override of `log` must call the parent's `log` method. Which technique best enforces this requirement in Python?

A.There is no built-in Python mechanism to enforce that an override calls the parent method; it must be handled by convention and documentation.
B.Decorate the parent's `log` method with `@abstractmethod`.
C.Use a metaclass that wraps the `log` method to check if `super().log` was called.
D.Define `log` in the parent class to raise `NotImplementedError` and document that subclasses must call `super().log()`.
AnswerA

Python does not provide a built-in way to enforce that a subclass override calls the superclass method. This is by design, as Python relies on conventions and developer discipline. While design patterns like the template method can encourage it, they do not enforce it. The correct answer acknowledges this limitation.

Why this answer

Python's philosophy emphasizes flexibility and developer responsibility. There is no language-level construct that forces a subclass to call `super().log()`. Techniques like abstract methods ensure implementation but not invocation of the parent.

Metaclasses or descriptors could be used to add checks, but they are not built-in and are overly complex. The correct answer recognizes that enforcement is not a built-in feature.

Exam trap

The trap here is assuming that abstract methods or other decorators can enforce that a subclass calls the parent method, when they cannot.

77
Matchingmedium

Match each list method to its effect.

Drag a concept onto its matching description — or click a concept then click the description.

Concepts
Matches

Adds x to end

Appends elements from iterable

Inserts x at index i

Removes first occurrence of x

Removes and returns last item

Why these pairings

Common list methods: append adds an element to the end, extend adds all elements from an iterable, remove deletes the first occurrence of a value, pop removes an element by index, and sort arranges the list in ascending order by default.

78
MCQmedium

Consider the following class hierarchy: class A: def method(self): return 'A'; class B(A): pass; class C(A): def method(self): return 'C'; class D(B, C): pass. What is the output of D().method() according to Python's MRO?

A.'B'
B.TypeError
C.'A'
D.'C'
AnswerD

C is the correct answer because C's method() is the first matching attribute encountered when traversing the MRO of the instance. Attribute lookup checks each class's __dict__ in the order determined by the C3 linearization, and since C is more specialized than its bases, its method() takes precedence. The call therefore dispatches to C's implementation, and the returned string is 'C'.

Why this answer

Python's MRO (Method Resolution Order) for class D(B, C) is computed using the C3 linearization algorithm, which respects the local precedence order and monotonicity. The MRO for D is D -> B -> C -> A, so D().method() resolves to C.method(), returning 'C'. Option D is correct because C is the first class in the MRO that defines method().

Exam trap

Python Institute often tests the C3 linearization rule that the MRO respects the order of base classes in the class definition, so candidates mistakenly think B's inheritance from A takes precedence over C's override, leading them to pick 'A' or 'B' instead of 'C'.

How to eliminate wrong answers

Option A is wrong because 'B' is not returned; class B does not override method(), so the MRO proceeds to C before reaching A. Option B is wrong because no TypeError occurs; the MRO is well-defined and method() is found in class C. Option C is wrong because 'A' is not returned; although A defines method(), the MRO finds C's override first, so A's version is never called.

79
MCQhard

Consider the following code snippet: 'class A: pass; class B(A): pass; class C(A): pass; class D(B, C): pass'. What is the Method Resolution Order (MRO) for class D according to the C3 linearization algorithm used by Python?

A.D, B, A, C, object
B.D, B, C, A
C.D, B, A, object, C
D.D, B, C, A, object
AnswerD

This is the correct C3 linearization for D(B, C) where both B and C inherit from A. The merge begins with D, then picks B because it is not in any tail; A is skipped next because it appears in the tail of C's linearization, forcing C to be emitted before A. After C is consumed, A and object are safe to append. The result preserves both parent MROs as subsequences and honors both local precedence order (B before C) and monotonicity, yielding D, B, C, A, object.

Why this answer

The C3 linearization algorithm merges the linearizations of D's parents (B and C) with their parent list, respecting the local precedence order and monotonicity. For class D(B, C), the MRO is computed as D + merge(L(B), L(C), [B, C]), where L(B) = B, A, object and L(C) = C, A, object. The merge yields D, B, C, A, object, which is the correct MRO.

Exam trap

Python Institute often tests the misconception that Python uses depth-first left-to-right resolution (like in old-style classes), leading candidates to pick Option A (D, B, A, C, object) instead of the correct C3 linearization result.

How to eliminate wrong answers

Option A is wrong because it incorrectly places A before C, violating the local precedence order where C should appear before A (since D inherits from B then C, and C is a direct parent). Option B is wrong because it omits 'object', which is always the final class in the MRO for new-style classes in Python. Option C is wrong because it places 'object' before C, which breaks monotonicity and the rule that a parent's MRO must be preserved; C must appear before object.

80
MCQhard

A class has a class attribute that is a list. A developer modifies this list via one instance, and the change is reflected in all other instances. What is the best practice to avoid this unintended sharing?

A.Use a tuple instead of a list.
B.Initialize the list in `__init__` rather than as a class attribute.
C.Use a class method to modify the list.
D.Use `deepcopy` when accessing the list.
AnswerB

Moving the list into `__init__` as `self.items = ...` causes a brand-new list to be created each time an instance is constructed. Because each object gets its own independent list bound to the instance, changes made through one instance never affect another. This is the standard Python pattern for per-instance mutable state and directly eliminates the accidental sharing caused by a class attribute.

Why this answer

Class attributes are shared across all instances. By initializing the list inside `__init__`, each instance gets its own independent list object, preventing unintended mutation from affecting other instances. This is the standard Python pattern for instance-specific mutable data.

Exam trap

Python Institute often tests the distinction between class-level and instance-level attributes, and the trap here is that candidates mistakenly think using a tuple (immutable) or a class method solves the sharing problem, when the real issue is the location of the mutable object's definition.

How to eliminate wrong answers

Option A is wrong because using a tuple prevents mutation entirely, which is not a solution for the requirement to modify the list; it changes the data structure's semantics and would cause an AttributeError on attempted modification. Option C is wrong because a class method still operates on the class-level list, so modifying it via one instance would still affect all instances; the sharing issue is not about the method type but about where the list is stored. Option D is wrong because `deepcopy` only creates a copy at the time of access, but the underlying class attribute remains shared; repeated accesses would require manual copying each time, which is inefficient and does not solve the fundamental design problem.

81
MCQhard

Refer to the exhibit. What is the output of the last line?

A.{'name': 'John'}
B.AttributeError: 'Employee' object has no attribute '__salary'
C.{'name': 'John', '__salary': 50000}
D.{'name': 'John', '_Employee__salary': 50000}
AnswerD

Name mangling transforms __salary into _Employee__salary at compile time, and this mangled name becomes the actual key in the instance's __dict__. Therefore, when the last line prints ex9myx.__dict__, it outputs {'name': 'John', '_Employee__salary': 50000}. This is exactly how Python implements pseudo-private attributes, and it is the correct and expected output for this code.

Why this answer

Python uses name mangling for attributes starting with double underscores (like `__salary`). When accessed via `vars()`, the mangled name `_Employee__salary` is shown, not the original `__salary`. The `vars()` function returns the `__dict__` of the object, which contains the mangled attribute name.

Exam trap

The PCAP exam often tests the misconception that `__salary` remains as-is in the object's dictionary, when in fact Python's name mangling renames it to `_Employee__salary` at compile time, and `vars()` reveals the mangled form.

How to eliminate wrong answers

Option A is wrong because it omits the mangled `__salary` attribute entirely, but `vars()` returns all instance attributes, including mangled ones. Option B is wrong because `__salary` is not accessed directly; `vars()` retrieves the attribute from the object's `__dict__`, which exists with the mangled name, so no AttributeError occurs. Option C is wrong because Python does not preserve the original `__salary` name in the instance dictionary; it mangles it to `_Employee__salary` to avoid accidental overriding in subclasses.

82
MCQeasy

Refer to the exhibit. What happens when the last line is executed?

A.The attribute 'city' is added dynamically.
B.The __slots__ is ignored because __init__ is defined.
C.A syntax error occurs.
D.An AttributeError is raised.
AnswerD

An AttributeError is raised because the class defines __slots__ = ('name', 'age'), and 'city' is not among the declared slots. Python then prevents the assignment of a new attribute, as instances with __slots__ do not have a __dict__ and cannot create undeclared attributes. This is the correct and intended behavior of __slots__.

Why this answer

When a class defines `__slots__`, it restricts attribute assignment to only those names listed in `__slots__`. Attempting to assign an attribute not in that list (like `city`) raises an `AttributeError` because the instance has no `__dict__` to store dynamic attributes. The `__init__` method does not override this restriction; it simply initializes the allowed slots.

Exam trap

Python Institute often tests the misconception that `__init__` can override `__slots__` or that `__slots__` only applies to class-level attributes, leading candidates to incorrectly choose option B or A.

How to eliminate wrong answers

Option A is wrong because `__slots__` explicitly prevents dynamic addition of attributes not listed in it, so `city` cannot be added dynamically. Option B is wrong because `__slots__` is not ignored when `__init__` is defined; `__init__` only initializes existing slots, it does not bypass the slot restriction. Option C is wrong because no syntax error occurs; the code is syntactically valid, and the error is raised at runtime when the assignment is executed.

83
MCQmedium

A Python class 'BankAccount' has a method 'withdraw(amount)' that deducts 'amount' from 'self.balance'. A developer writes a subclass 'SavingsAccount' that overrides 'withdraw' to add a penalty if balance drops below minimum. Which design pattern is being used?

A.Composition
B.Aggregation
C.Method overriding
D.Inheritance
AnswerC

SavingsAccount redefines the inherited withdraw method with its own penalty logic while keeping the same signature, so Python dispatches to the subclass version at runtime. That substitution of a superclass method implementation is method overriding, not overloading or composition.

Why this answer

Method overriding is the mechanism where a subclass provides a specific implementation of a method that is already defined in its superclass. In this scenario, SavingsAccount overrides the withdraw method from BankAccount to add penalty logic, which is the defining characteristic of method overriding in Python.

Exam trap

The trap here is that candidates often confuse inheritance (the relationship) with method overriding (the specific technique), leading them to select 'Inheritance' instead of 'Method overriding' when the question explicitly describes a subclass redefining a parent method.

How to eliminate wrong answers

Option A is wrong because composition is a design pattern where a class contains instances of other classes as members to achieve code reuse, not where a subclass redefines a parent method. Option B is wrong because aggregation is a special form of composition representing a 'has-a' relationship with a weaker ownership lifecycle, not the act of overriding a method. Option D is wrong because inheritance is the broader mechanism that allows SavingsAccount to derive from BankAccount, but the specific pattern described—redefining withdraw in the subclass—is method overriding, not inheritance itself.

84
MCQhard

Which of the following best describes the use of the @abstractmethod decorator in Python?

A.It automatically provides a default implementation for a method.
B.It indicates that a method must be overridden in any non-abstract subclass.
C.It creates a method that can be called without an instance.
D.It raises AttributeError if the method is called.
AnswerB

Decorating a method with @abstractmethod registers it in the class's __abstractmethods__ set, and the ABCMeta metaclass enforces that every concrete (non-abstract) subclass must provide an overriding implementation. This enforcement happens at instantiation time: attempting to create an instance of a subclass that leaves any abstract method unimplemented raises TypeError. Thus the decorator expresses a contract that subclasses must fulfill, not a prefix to an automatically inherited implementation.

Why this answer

The @abstractmethod decorator, when used in conjunction with a metaclass like ABC (Abstract Base Class) or by inheriting from ABC, declares a method as abstract. This forces any concrete (non-abstract) subclass to provide an override of that method; otherwise, Python raises a TypeError at instantiation time. Option B correctly captures this mandatory override requirement.

Exam trap

The PCAP exam often tests the distinction between 'must be overridden' and 'can be overridden' — the trap here is that candidates confuse @abstractmethod with a method that simply raises NotImplementedError, which does not enforce overriding at instantiation time.

How to eliminate wrong answers

Option A is wrong because @abstractmethod does not provide any default implementation; it explicitly marks a method as having no implementation in the base class. Option C is wrong because @abstractmethod does not create a static or class method; it is used for instance methods by default, and calling it without an instance would still require the method to be bound to the class, not automatically callable without an instance. Option D is wrong because @abstractmethod does not raise AttributeError when the method is called; instead, if a concrete subclass fails to override it, Python raises a TypeError at instantiation time, not when the method is called.

85
MCQeasy

A company needs to model different types of employees. They have a base class `Employee` with a method `calculate_pay()`. For hourly employees, pay = hours * rate; for salaried employees, pay = salary. Which design approach is most appropriate?

A.Use a `@staticmethod` inside `Employee` to compute pay based on a type parameter.
B.Define a module-level function that takes an employee object and computes pay.
C.Create subclasses `HourlyEmployee` and `SalariedEmployee` that override `calculate_pay()`.
D.Use a single `Employee` class with conditional statements to differentiate pay types.
AnswerC

Subclasses overriding `calculate_pay()` let each employee type supply its own formula, so hourly pay computes hours × rate while salaried returns the fixed salary. This satisfies the stem's requirement to model distinct employee types sharing one interface, since Python dispatches the overridden method polymorphically at runtime based on the instance's actual class.

Why this answer

It applies polymorphism through method overriding: each subclass (`HourlyEmployee`, `SalariedEmployee`) provides its own implementation of `calculate_pay()`, allowing the calling code to treat all employees uniformly via the base class interface. This adheres to the Open/Closed Principle and keeps the design extensible without modifying existing code when new employee types are added.

Exam trap

Python Institute often tests the distinction between using inheritance with method overriding versus using conditionals or static methods, and the trap here is that candidates may think a single class with `if` statements is simpler and therefore better, missing the long-term maintenance and extensibility advantages of polymorphism.

How to eliminate wrong answers

Option A is wrong because a `@staticmethod` cannot access instance attributes (`hours`, `rate`, `salary`) without passing them explicitly, and using a type parameter violates polymorphism by requiring conditional logic inside the static method. Option B is wrong because a module-level function breaks encapsulation and does not leverage the class hierarchy, making it harder to extend and maintain as employee types grow. Option D is wrong because using conditional statements inside a single `Employee` class violates the Open/Closed Principle and leads to fragile code that must be modified every time a new pay type is introduced.

86
MCQmedium

A class 'Point' is defined with __slots__ = ['x', 'y']. A developer creates an instance p = Point() and tries to set p.z = 10. What happens?

A.The assignment is silently ignored
B.Python issues a warning and adds the attribute
C.The attribute 'z' is added dynamically
D.AttributeError is raised
AnswerD

AttributeError is raised is correct because when a class defines __slots__ without including '__dict__', instances are not given a dictionary for arbitrary attributes. The assignment a.z = ... invokes the instance's __setattr__ method, which finds no descriptor for 'z' and no __dict__ in which to store it, so Python raises AttributeError: 'Point' object has no attribute 'z'. This is a deliberate design choice: slots save memory and enforce a fixed attribute schema by rejecting any attribute not explicitly listed.

Why this answer

When a class defines `__slots__`, it restricts attribute assignment to only those names listed in the tuple. Attempting to assign an attribute not in `__slots__` raises an `AttributeError`. This is a deliberate memory optimization that prevents the creation of a per-instance `__dict__`, so dynamic attributes are disallowed.

Exam trap

Python Institute often tests the misconception that `__slots__` only provides a hint or that attributes can still be added dynamically, when in fact it strictly forbids any attribute not listed in `__slots__`.

How to eliminate wrong answers

Option A is wrong because the assignment is not silently ignored; Python actively raises an exception. Option B is wrong because Python does not issue a warning and add the attribute; it strictly enforces the slot restriction with an error. Option C is wrong because `__slots__` prevents dynamic attribute addition; the attribute 'z' is not added under any circumstances.

87
MCQeasy

A developer defines a class 'Car' with an attribute 'wheels = 4'. They create two instances: car1 = Car() and car2 = Car(). Then they set car1.wheels = 3. What is the value of car2.wheels?

A.4
B.3
C.AttributeError
D.None
AnswerA

Since car2 never receives an instance attribute named wheels, Python's attribute lookup falls back to the class attribute defined on Car. At the time car2.wheels is evaluated, that class attribute still holds the integer 4, because the assignment car1.wheels = 3 only adds an entry to car1's __dict__, leaving the class-level value untouched. Therefore the correct result is 4, not 3, None, or an error.

Why this answer

In Python, setting an attribute directly on an instance (car1.wheels = 3) creates an instance attribute that shadows the class attribute for that specific instance only. The class attribute 'wheels = 4' remains unchanged and is still accessible via car2.wheels, which has not been overridden. Therefore, car2.wheels retains the class-level value of 4.

Exam trap

Python Institute often tests the distinction between class attributes and instance attributes, trapping candidates who mistakenly think that modifying an instance attribute propagates to other instances or that it raises an error.

How to eliminate wrong answers

Option B is wrong because it assumes that modifying an instance attribute on one object affects all instances of the class, which is not true in Python; instance attribute assignments are local to that instance. Option C is wrong because no AttributeError occurs: car2.wheels successfully resolves to the class attribute 'wheels' defined in the Car class. Option D is wrong because car2.wheels is not None; it is explicitly set to 4 at the class level and never reassigned.

88
MCQeasy

A junior developer wrote a class representing a bank account with a private attribute balance. They used double underscore prefix (__balance) to make it private. However, in a test script, they are still able to access the attribute using the mangled name _Account__balance. The developer is confused about why encapsulation is not enforced. Which statement best explains this behavior?

A.The double underscore prefix actually makes the attribute completely inaccessible from outside the class.
B.Python's name mangling is only a convention and does not prevent access.
C.The developer forgot to use the @property decorator.
D.The test script must have used a different class attribute name.
AnswerB

Name mangling is a syntactic transformation, not a security mechanism: `self.__balance` inside a class becomes `self._BankAccount__balance`. It does not prevent external code from accessing the attribute via that mangled name, so it is only a convention. It primarily helps avoid accidental overrides in subclasses rather than hiding data.

Why this answer

Python's name mangling (triggered by a double underscore prefix) is not a security mechanism but a syntactic transformation that renames the attribute to _ClassName__attribute. This prevents accidental name clashes in subclasses but does not enforce true encapsulation; the attribute can still be accessed via the mangled name from outside the class. The developer's confusion stems from mistaking name mangling for a privacy guarantee, which Python intentionally does not provide.

Exam trap

The trap here is that Python Institute often tests the misconception that double underscore prefix enforces true privacy like in languages such as Java or C++, when in reality Python's name mangling is merely a renaming convention that does not prevent access from outside the class.

How to eliminate wrong answers

Option A is wrong because the double underscore prefix does not make the attribute completely inaccessible; it only triggers name mangling, and the attribute remains accessible via the mangled name (e.g., _Account__balance). Option C is wrong because the @property decorator is used to define getter/setter methods for controlled access, but its absence does not affect the ability to access the attribute directly via the mangled name; the core issue is about privacy enforcement, not property decorators. Option D is wrong because the test script correctly uses the mangled name _Account__balance, which is the actual attribute name after mangling; there is no different class attribute name involved.

89
MCQmedium

Consider the following code: class A: x = 1; class B(A): pass; class C(A): x = 2; class D(B, C): pass. What is D.x?

A.AttributeError
B.1
C.2
D.(1, 2) tuple
AnswerC

C's x = 2 is the correct result because attribute lookup walks D.__mro__ in order: D, B, C, A, object. B adds no x attribute, so the first slot that defines x is C, and its value 2 is returned immediately. This is exactly how the C3 linearization resolves the diamond: the direct base class C is visited before the shared ancestor A. The number 2 therefore comes from the local precedence of C over A in the class definition D(B, C).

Why this answer

Python's method resolution order (MRO) for class D, which uses multiple inheritance, follows the C3 linearization algorithm. The MRO for D is D, B, C, A, and since C defines x = 2, that attribute overrides the x = 1 from A in the inheritance chain, so D.x resolves to 2.

Exam trap

Python Institute often tests the misconception that Python's multiple inheritance simply merges attributes from all parent classes, leading candidates to expect a tuple or an error, rather than applying the C3 linearization to determine a single overriding value.

How to eliminate wrong answers

Option A is wrong because D inherits from B and C, which both ultimately inherit from A, so x is defined in the hierarchy and no AttributeError occurs. Option B is wrong because although A defines x = 1, the MRO prioritizes C's definition of x = 2 over A's, so D.x is not 1. Option D is wrong because attribute lookup in Python does not return a tuple of all inherited values; it returns a single value from the first class in the MRO that defines the attribute.

90
MCQhard

Refer to the exhibit. What is the root cause of the error?

A.The attribute 'age' is not defined in the class or instance.
B.The __init__ method was not called.
C.The attribute 'age' is private.
D.The attribute 'age' is a class attribute but not initialized.
AnswerA

The attribute lookup for 'age' fails because it was never set on the instance or the class. When an object attribute is accessed, Python first checks the instance's __dict__, then the class and its base classes; since neither 'self.age' in __init__ nor a class-level 'age' exists, AttributeError is raised. The instance only has 'name' assigned, so any other attribute name, including 'age', is undefined.

Why this answer

The error 'AttributeError: 'ClassName' object has no attribute 'age'' occurs when code attempts to access an instance attribute that has never been defined. In Python, attributes must be assigned (e.g., in __init__ or directly on the instance) before they can be read; simply declaring a class-level variable named 'age' would not cause this error, but if no assignment exists, the attribute is missing.

Exam trap

The PCAP exam often tests the distinction between a missing attribute (AttributeError) and an uninitialized class attribute (which would not raise an error), tempting candidates to pick Option D when the real issue is that the attribute was never defined at all.

How to eliminate wrong answers

Option B is wrong because even if __init__ is never called, the error is not about the method being uncalled—it is about the attribute 'age' never being assigned anywhere. Option C is wrong because Python does not have true private attributes; name mangling (double underscore prefix) would change the attribute name to _ClassName__age, but the error message would still reference the mangled name, not 'age'. Option D is wrong because a class attribute that is not initialized would still exist (defaulting to the name 'age' in the class dict) and would not cause an AttributeError; the error only occurs when the attribute is completely absent from both the instance and the class.

91
MCQmedium

A programmer wants to create a class that cannot be instantiated directly, only through a factory method. Which approach should be used?

A.Raise an exception in __init__ if called directly.
B.Define the class as abstract using ABC and @abstractmethod.
C.Override __new__ to raise an exception unless called from a classmethod factory.
D.Use a metaclass to prevent instantiation.
AnswerC

As the actual instance-creation hook, __new__ runs before __init__; raising an exception there prevents the instance from ever coming into existence. A classmethod factory can be written to call cls.__new__(cls) while suppressing the guard (e.g., through a private flag), enabling controlled creation. This pattern is the standard way to enforce a factory-only or singleton design, because direct calls to Class() are intercepted at the earliest point.

Why this answer

Overriding `__new__` allows the programmer to control instance creation at the lowest level. By checking the call stack or a flag set by a classmethod factory, `__new__` can raise an exception when instantiation is attempted directly, while still allowing the factory method to create instances. This ensures the class cannot be instantiated directly, only through the designated factory.

Exam trap

Python Institute often tests the distinction between `__new__` and `__init__`, and the trap here is that candidates think raising an exception in `__init__` (Option A) is sufficient, not realizing that `__new__` has already created the object and the exception only prevents full initialization, not allocation.

How to eliminate wrong answers

Option A is wrong because raising an exception in `__init__` still allows the object to be partially created (memory allocated by `__new__`), and the exception can be caught, leaving a half-initialized object or causing confusion; it does not prevent instantiation at the allocation stage. Option B is wrong because defining a class as abstract with ABC and @abstractmethod prevents instantiation only if the class has unimplemented abstract methods; if all abstract methods are implemented, the class can be instantiated directly, which does not enforce the factory-only requirement. Option D is wrong because using a metaclass to prevent instantiation is overly complex and not a standard Python pattern for this specific need; it would require overriding `__call__` in the metaclass, which is less direct and more error-prone than overriding `__new__` in the class itself.

92
MCQhard

A class `ServerConfig` has a class attribute `port = 8080`. After deployment, a developer runs `ServerConfig.port = 9090` in one module, and unexpectedly all existing instances now use port 9090. What concept explains this behavior?

A.Instance attributes always override class attributes.
B.The attribute is immutable.
C.Class attributes are shared among all instances of a class.
D.Python uses copy-on-write for attribute access.
AnswerC

Class attributes live on the class object itself, so every instance resolves `port` through the class unless it defines its own. Rebinding `ServerConfig.port = 9090` mutates that single shared slot, which is why all existing instances immediately report 9090 without any instance-level assignment.

Why this answer

Class attributes in Python are shared across all instances of a class. When you modify `ServerConfig.port` on the class itself, every instance that accesses `port` via the class (or via an instance that hasn't overridden it) sees the new value. This is fundamental to Python's attribute lookup mechanism: instance attributes shadow class attributes, but if no instance attribute exists, the class attribute is used.

Exam trap

Python Institute often tests the distinction between modifying a class attribute via the class vs. modifying it via an instance; the trap is that candidates think assigning to `instance.port` changes the class attribute, but it actually creates a new instance attribute that shadows the class attribute.

How to eliminate wrong answers

Option A is wrong because instance attributes only override class attributes when they are explicitly set on the instance; they do not cause class-level changes to propagate. Option B is wrong because the attribute `port` is an integer, which is immutable, but immutability does not affect sharing or rebinding of the attribute on the class. Option D is wrong because Python does not use copy-on-write for attribute access; it uses a dynamic lookup chain (instance → class → parent classes) and assignment always modifies the target directly.

93
MCQeasy

A class has a method that does not access any instance or class data. What decorator should be used to define it as a utility method that can be called on the class itself without requiring an instance?

A.@property
B.@staticmethod
C.@abstractmethod
D.@classmethod
AnswerB

@staticmethod is correct because it explicitly declares that the method does not receive the implicit first argument—neither `self` nor `cls`—so it can be defined and called without any reference to instance or class state. It is still stored on the class for organizational purposes, but behaves essentially like a plain function in terms of argument binding. This makes it the exact decorator for a method that accesses no instance or class data.

Why this answer

A @staticmethod decorator defines a method that does not receive an implicit first argument (self or cls). It can be called on the class itself without creating an instance, making it suitable for utility functions that do not access instance or class data. This matches the requirement.

Exam trap

PCAP often tests the confusion between @staticmethod and @classmethod, where candidates might think @classmethod is for utility methods, but it actually receives the class as an argument.

How to eliminate wrong answers

Option A is wrong because @property is used to define a method as a getter for an attribute, allowing it to be accessed like an attribute, but it still requires an instance and typically accesses instance data. Option C is wrong because @abstractmethod is used to declare abstract methods in abstract base classes, which must be overridden by subclasses; it does not make a method callable on the class without an instance. Option D is wrong because @classmethod defines a method that receives the class as the first argument (cls), and while it can be called on the class, it is intended for methods that need to access or modify class state, not for utility methods that do not access any data.

94
Multi-Selectmedium

Which TWO statements about method overriding in Python are correct?

Select 2 answers
A.The overriding method can call the parent method using super()
B.The overriding method must have the same name
C.The overriding method requires explicit use of the @override decorator
D.The overriding method must have the exact same parameter list
E.Overriding is resolved at compile time
AnswersA, B

In a subclass, super() returns a proxy bound to the parent classes in the method resolution order, allowing the overriding method to explicitly invoke the version it replaced. Calling super().method(*args) inside the override is idiomatic when you want to extend, rather than completely replace, the inherited behavior. This capability does not make super() mandatory; a subclass may fully override a method without calling its parent at all.

Why this answer

The overriding method can call the parent class's version of the method using the built-in `super()` function, which returns a proxy object that delegates method calls to the parent class. This is a fundamental feature of Python's inheritance mechanism, allowing the child method to extend or modify the parent's behavior while still invoking it.

Exam trap

Python Institute often tests the misconception that Python enforces strict method signatures or requires an `@override` decorator, leading candidates to select options D or C, when in fact Python's dynamic nature allows flexible overriding without compile-time checks.

95
MCQhard

A developer wants to ensure that the 'radius' attribute of a Circle class is always non-negative. Which implementation using @property is correct?

A.class Circle:\n def __init__(self, radius):\n self._radius = radius\n def radius(self):\n return self._radius
B.class Circle:\n def __init__(self, radius):\n self._radius = radius\n @property\n def radius(self):\n return self._radius\n @radius.setter\n def radius(self, value):\n if value < 0:\n raise ValueError\n self._radius = value
C.class Circle:\n def __init__(self, radius):\n self.radius = radius\n @property\n def radius(self):\n return self._radius\n @radius.setter\n def radius(self, value):\n if value < 0:\n raise ValueError\n self._radius = value
D.class Circle:\n def __init__(self, radius):\n self.radius = radius\n @property\n def radius(self):\n return self._radius\n def set_radius(self, value):\n self._radius = value
AnswerC

The correct pattern routes initial assignment through the property setter because __init__ says self.radius = radius, not self._radius = radius. Every time radius is set, regardless of whether it's during construction or later, the setter checks the value and raises ValueError for a negative input. The getter then simply returns the validated _radius, giving a clean, consistent public attribute.

Why this answer

It uses the @property decorator to define a getter and @radius.setter to define a setter that validates the radius value before assignment. The __init__ method correctly assigns to self.radius, which triggers the setter, ensuring the initial value is also validated. This pattern enforces the invariant that radius is always non-negative.

Exam trap

Python Institute often tests the subtlety that __init__ must use the property setter (self.radius = radius) rather than directly assigning to the backing attribute (self._radius = radius) to ensure validation applies to the initial value as well.

How to eliminate wrong answers

Option A is wrong because it lacks the @property decorator, so radius is a plain method, not a property; accessing circle.radius would return a bound method, not the stored value. Option B is wrong because __init__ assigns to self._radius directly, bypassing the setter validation, so an initial negative radius would be accepted. Option D is wrong because it defines a set_radius method instead of using @radius.setter, so the property is read-only and assignment to circle.radius would raise an AttributeError.

96
MCQhard

A Python developer is implementing a class that should behave like a sequence and support indexing. Which pair of special methods must be defined to achieve this?

A.__getitem__ and __contains__
B.__setitem__ and __delitem__
C.__iter__ and __next__
D.__getitem__ and __len__
AnswerD

According to the Python data model, a sequence is defined primarily by having a `__len__` method and a `__getitem__` method that accepts integer indices (and optionally slices). Together they give the object a finite length, support `obj[i]` indexing, and implicitly enable iteration via the old-style `__getitem__` fallback. On top of these two, the `collections.abc.Sequence` ABC automatically mixes in `__contains__`, `__iter__`, `__reversed__`, `index`, and `count`, showing how much behavior is derived from just these two methods.

Why this answer

To make a class behave like a sequence and support indexing (e.g., obj[0]), Python requires the __getitem__ method to retrieve items by key. Additionally, the __len__ method is needed to define the length of the sequence, which is used by built-in functions like len() and is part of the sequence protocol. Together, these two methods satisfy the minimal requirements for a sequence-like object that supports indexing.

Exam trap

Python Institute often tests the distinction between the sequence protocol (__getitem__ + __len__) and the iterator protocol (__iter__ + __next__), trapping candidates who think iteration alone enables indexing.

How to eliminate wrong answers

Option A is wrong because __contains__ is used for the 'in' operator (membership testing), not for indexing or sequence behavior. Option B is wrong because __setitem__ and __delitem__ are for mutable sequences that support item assignment and deletion, but indexing (read access) only requires __getitem__; __len__ is still needed for sequence protocol. Option C is wrong because __iter__ and __next__ implement the iterator protocol, which allows iteration but does not provide indexing (e.g., obj[0] would fail without __getitem__).

97
MCQmedium

A class named Counter has a class variable count and an instance method increment. The method is defined as def increment(self): Counter.count += 1. What will be the output after executing the following code? c1 = Counter(); c2 = Counter(); c1.increment(); c2.increment(); print(Counter.count, c1.count, c2.count)

A.2 2 2
B.2 1 1
C.1 1 1
D.0 0 0
AnswerA

The output is 2 2 2 because `count` is a class variable defined on the `Counter` class. Each call to `increment()` modifies the same class-level attribute, not a per-instance copy, so after two increments the shared value becomes 2. Both `c1.count` and `c2.count` resolve to that same class attribute via Python's attribute lookup chain, and `Counter.count` directly reads it, so all three expressions print the identical value 2.

Why this answer

The class variable `count` is shared across all instances of the `Counter` class. The `increment` method modifies `Counter.count` directly, so after two calls, `Counter.count` becomes 2. Since `c1.count` and `c2.count` refer to the same class variable (they do not have instance attributes shadowing it), both instances reflect the same value of 2.

Exam trap

Python Institute often tests the distinction between class variables and instance variables, trapping candidates who mistakenly think each instance gets its own copy of the class variable or that `self.count` would create an instance attribute instead of modifying the class variable.

How to eliminate wrong answers

Option B is wrong because it assumes each instance has its own `count` attribute that increments independently, but the code modifies the class variable, not instance attributes. Option C is wrong because it suggests only one increment occurred, but two calls to `increment()` were made. Option D is wrong because it implies no increment happened at all, ignoring the two calls to `increment()`.

98
Multi-Selecthard

Refer to the exhibit. Which THREE statements about the class hierarchy are correct?

Select 3 answers
A.D does not have a method named 'method'
B.Calling D().method() returns 'B'
C.The super() call in B would call C.method
D.The MRO of D is ['D', 'B', 'C', 'A', 'object']
E.Class C is a subclass of A
AnswersB, D, E

Calling D().method() returns 'B' because the MRO for D is ['D', 'B', 'C', 'A', 'object']. Since D defines no method, Python's lookup proceeds to B, whose method returns the string 'B' directly, without calling super(). Therefore C.method is never reached, and the call evaluates to the string 'B'.

Why this answer

When `D().method()` is called, Python's MRO (Method Resolution Order) for class D is `['D', 'B', 'C', 'A', 'object']`. Since class D does not define `method`, Python looks up the MRO and finds `method` first in class B. The `return 'B'` in B's `method` is executed, so the call returns 'B'.

Exam trap

Python Institute often tests the misconception that `super()` always calls the immediate parent class, when in fact it follows the MRO of the runtime instance, which can skip or reorder classes in multiple inheritance scenarios.

99
MCQeasy

A developer writes a class `Circle` with `def __init__(self, radius): self.radius = radius` and wants printing a `Circle` instance to produce a readable description such as `Circle(5)`. Which method should be implemented, and why over the alternative?

A.Implement `__repr__`, because it provides the unambiguous representation used by the interactive interpreter, `repr()`, and containers, and `__str__` falls back to it when undefined.
B.Implement `__str__`, because it is the method the `print()` built-in always calls for any object.
C.Implement `__init__` to return a formatted string, because the constructor runs whenever the object is displayed.
D.Implement `__format__`, because string formatting is the modern replacement for both `__str__` and `__repr__`.
AnswerA

`__repr__` is the canonical developer-facing representation and is what the interactive interpreter, `repr()`, and container displays use. When `__str__` is not defined, `print()` falls back to `__repr__`, so implementing it alone yields `Circle(5)` in every context described. This single method covers both the debugging and printing needs of the scenario without duplication.

Why this answer

`__repr__` is the unambiguous, developer-oriented representation that the interpreter, `repr()`, and containers use, and Python falls back to it for `print()` when `__str__` is absent. Implementing it alone therefore produces `Circle(5)` in every context the scenario mentions. `__str__` is the user-facing alternative, `__format__` governs format-spec handling, and `__init__` has nothing to do with display.

Exam trap

The trap here is assuming `print()` requires `__str__`, when `print()` actually falls back to `__repr__` if `__str__` is not defined.

100
MCQmedium

A class inherits from two parent classes that both have a method with the same name. When calling the method on the child, only one parent's version is executed. What Python mechanism determines which one?

A.Method overloading by signature.
B.Explicit super() call in the child class.
C.Inheritance depth (closest parent wins).
D.Method Resolution Order (MRO).
AnswerD

Python resolves the conflict using the Method Resolution Order, the linearised sequence computed by the C3 algorithm. It determines which parent's method is invoked, so the first matching class in that order executes, explaining why only one parent's version runs.

Why this answer

Python uses the C3 linearization algorithm to compute the Method Resolution Order (MRO) for a class. When a method is called on an instance, Python searches the MRO from left to right and executes the first implementation it finds. This ensures a consistent and predictable order of inheritance, even in diamond or multiple-inheritance scenarios.

Exam trap

Python Institute often tests the misconception that Python uses 'closest parent wins' or depth-first search, but the actual mechanism is the C3 linearization MRO, which respects base class order and the diamond inheritance pattern.

How to eliminate wrong answers

Option A is wrong because Python does not support method overloading by signature; the last definition of a method in a class overwrites previous ones, and dispatch is based on the object's type, not argument types. Option B is wrong because an explicit super() call is a way to invoke a parent's method from within the child, but it is not the mechanism that determines which parent's method is executed when calling the method directly on the child. Option C is wrong because inheritance depth does not determine which parent's method is called; Python's MRO follows the C3 linearization order, which respects the order of base classes and the diamond pattern, not simply the closest parent.

101
Multi-Selectmedium

Which TWO of the following are valid ways to define a class method in Python? (Select exactly two.)

Select 2 answers
A.def my_method(cls): pass my_method = classmethod(my_method)
B.class MyClass: def my_method(cls): pass
C.def my_method(self): pass
D.@staticmethod def my_method(cls): pass
E.@classmethod def my_method(cls): pass
AnswersA, E

This is a valid, if less common, manual application of the classmethod() built-in. The module-level function is defined with a first parameter named cls, and then classmethod(my_method) wraps that function in a classmethod descriptor before assigning it back to the same name. From that point on, accessing the attribute on the class or on an instance binds the class object as the first argument, exactly as the @classmethod decorator would do at class creation time.

Why this answer

It manually applies the `classmethod()` built-in function to a regular function, converting it into a class method. This is a valid, though less common, way to define a class method, as the `classmethod` descriptor wraps the function so that the first argument passed is the class (`cls`), not an instance.

Exam trap

Python Institute often tests the distinction between the explicit `classmethod()` call and the decorator syntax, and the trap here is that candidates may think only the `@classmethod` decorator is valid, overlooking the manual `classmethod()` function call as an equally valid alternative.

102
MCQmedium

Refer to the exhibit. What will happen when the code is executed?

A.It prints None
B.It raises a TypeError
C.It raises an AttributeError
D.It prints 10
AnswerC

The correct outcome is an AttributeError because of Python name mangling. Any identifier of the form __x (two leading underscores, at most one trailing underscore) inside a class is rewritten to _ClassName__x. Since the object has an attribute _ClassName__x and not __x, the direct access obj.__x cannot find the attribute, and Python raises AttributeError with a message like 'Test' object has no attribute '__x'.

Why this answer

The code attempts to call a method or access an attribute that does not exist on the object. In Python, when you try to access an attribute or method that is not defined on an object, an AttributeError is raised. Option C is correct because the exhibit shows an attempt to call a non-existent method or attribute on an instance, which triggers AttributeError.

Exam trap

Python Institute often tests the distinction between AttributeError and TypeError, trapping candidates who confuse missing methods with type mismatches.

How to eliminate wrong answers

Option A is wrong because the code does not contain a return statement that would produce None; instead, it raises an exception. Option B is wrong because a TypeError occurs when an operation or function is applied to an object of inappropriate type, not when a missing attribute is accessed. Option D is wrong because the code does not print 10; it raises an exception before any print statement could execute.

103
MCQeasy

A class has an attribute that should be computed on access and cached for subsequent accesses. Which pattern is most appropriate?

A.Use __slots__ to define the attribute.
B.Use a custom descriptor that caches the value in the instance's __dict__.
C.Use a @classmethod to compute the value.
D.Use a @staticmethod to compute the value.
AnswerB

A custom descriptor that implements __get__ is the right approach because it can compute the value on first access and then store that computed result in the instance's __dict__ under a private key or even the same name if it is a non-data descriptor. On subsequent accesses, the instance dictionary takes precedence over the non-data descriptor, so the cached value is returned directly without recomputation. This exactly matches the desired lazy, one-time computation behavior.

Why this answer

A custom descriptor with a __get__ method can compute the attribute value on first access and store it in the instance's __dict__ for subsequent accesses. This pattern, often called a cached property, ensures the computation happens only once per instance, which is exactly what the question requires.

Exam trap

Python Institute often tests the distinction between descriptors that compute on every access versus those that cache, and the trap here is that candidates confuse __slots__ (memory optimization) with caching, or think that class-level methods like @classmethod can somehow provide per-instance caching.

How to eliminate wrong answers

Option A is wrong because __slots__ is used to restrict the attributes an instance can have and to save memory by preventing the creation of a __dict__; it does not provide any caching or lazy computation mechanism. Option C is wrong because a @classmethod operates on the class itself, not on an instance, and cannot cache per-instance computed values. Option D is wrong because a @staticmethod is essentially a regular function inside the class namespace, with no access to the instance or class, and thus cannot compute or cache instance-specific attributes.

104
MCQmedium

A developer defines a class hierarchy with multiple inheritance: class A, class B(A), class C(A), class D(B,C). The method 'm' is defined only in A. What is the method resolution order for D according to the C3 linearization algorithm?

A.D -> B -> A -> object
B.D -> B -> C -> A -> object
C.D -> B -> C -> A
D.D -> B -> A -> C
AnswerB

This is the correct C3 linearization. For D(B, C), B(A), C(A), and A(object), the merge starts with D, then B's MRO [B, A, object] and C's MRO [C, A, object] are merged with the direct bases [B, C]. B is taken first, then C (because C is not in the tail of B's MRO), then A (because A is not in the tail of any remaining list), and finally object. This order preserves local precedence order (B before C), respects the inheritance chain C before A, and includes object as the final class.

Why this answer

B is correct because the C3 linearization algorithm always includes 'object' at the end of the MRO. For class D(B, C), L(D) = D + merge(L(B), L(C), [B, C]). L(B) = B, A, object; L(C) = C, A, object.

Merge yields D, B, C, A, object. Options that omit 'object' (like C) are incomplete.

Exam trap

Python Institute often tests the misconception that the MRO follows a simple depth-first left-to-right order, causing candidates to pick option D (D, B, A, C) instead of the correct C3 merge result. Additionally, candidates may forget that 'object' is always included in the MRO, leading them to choose option C over B.

How to eliminate wrong answers

Option A is wrong because it omits class C entirely, violating the local precedence order of D's bases (B, C) and the C3 merge rule that includes all parent classes. Option B is wrong because it places object at the end, which is technically correct but the given option list omits 'object' — however the core error is that it includes object while the correct answer (C) does not; more importantly, option B's order (D, B, C, A, object) is actually the full correct MRO, but the question's answer choices treat 'object' as optional, so option C is the intended correct answer without object. Option D is wrong because it places A before C, violating the C3 rule that C must appear before A since C inherits from A and the merge must preserve the order from C's linearization (C, A).

105
Multi-Selecthard

Which TWO of the following are valid ways to define a class attribute that is shared among all instances?

Select 2 answers
A.Assign the attribute inside a classmethod using cls.
B.Assign the attribute inside a staticmethod.
C.Use the @property decorator.
D.Assign the attribute inside __init__ using self.
E.Assign the attribute in the class body, outside any methods.
AnswersA, E

A classmethod receives the class object as its first parameter, conventionally named cls, so assigning cls.attribute = value inside the method writes directly into the class's namespace and creates a genuine class attribute shared by every instance. This is valid as long as the method is executed at least once (for example, called after class creation). Because cls is the actual class, the attribute is also inherited correctly by subclasses.

Why this answer

A classmethod receives the class (cls) as its first argument, allowing you to assign or modify a class attribute via `cls.attribute = value`. This assignment affects the class itself, making the attribute shared among all instances, as the attribute is stored on the class object, not on instance dictionaries.

Exam trap

Python Institute often tests the distinction between class-level and instance-level attributes, and the trap here is that candidates confuse the @property decorator (which defines a computed instance property) with a class attribute, or think that a staticmethod can modify class state because it is defined inside the class body.

106
MCQhard

A developer creates a metaclass 'Meta' that modifies class creation by adding a class attribute 'created_by' set to 'Meta'. Which code snippet correctly defines and uses this metaclass?

A.class Meta(type): def __new__(cls, name, bases, dct): dct['created_by']='Meta'
B.class Meta(type): def __init__(cls, name, bases, dct): dct['created_by']='Meta'
C.def Meta(name, bases, dct): dct['created_by']='Meta'; return type(name, bases, dct)
D.class Meta(type): def __new__(cls, name, bases, dct): dct['created_by']='Meta'; return super().__new__(cls, name, bases, dct)
AnswerD

This correctly overrides `type.__new__`, modifies the mutable namespace dictionary before class construction, and delegates to `super().__new__` to build the class object. Because `super().__new__` copies the entries of `dct` into the new class's `__dict__`, the added `created_by` attribute is present on the finished class. The explicit `return` is essential: without it, no class object would be created at all.

Why this answer

It defines a proper metaclass by subclassing `type` and overriding `__new__`, which is the correct method for modifying the class dictionary before the class is created. The `__new__` method must return the result of `super().__new__(cls, name, bases, dct)` to actually create the class object. Adding `dct['created_by']='Meta'` inside `__new__` ensures the attribute is set during class creation.

Exam trap

Python Institute often tests the distinction between `__new__` and `__init__` in metaclasses, and the trap here is that candidates mistakenly think `__init__` can modify the class dictionary before class creation, or forget that `__new__` must explicitly return the class object.

How to eliminate wrong answers

Option A is wrong because the `__new__` method does not return the newly created class object; without `return super().__new__(...)`, the metaclass returns `None`, causing a `TypeError` when trying to create a class. Option B is wrong because `__init__` is called after the class is already created, so modifying `dct` inside `__init__` does not affect the class's attributes (the dictionary is already used); the correct place to modify the class dictionary is in `__new__`. Option C is wrong because it defines a regular function, not a metaclass; although it can create a class dynamically, it does not define a metaclass that can be used with the `metaclass=Meta` keyword argument in a class statement.

107
MCQeasy

A developer wants to ensure that a class attribute is shared across all instances. Which approach should be used?

A.Use the @property decorator.
B.Define the attribute inside a method without using self.
C.Define the attribute in the class body, outside any methods.
D.Define the attribute inside __init__ using self.
AnswerC

Assigning a value in the class body, directly at indentation level of the class, creates a class attribute stored on the class object. All instances of that class share the same object through normal attribute lookup; accessing it via an instance falls back to the class if no instance attribute shadows it. This is the idiomatic way to create shared state used by every instance.

Why this answer

Defining an attribute in the class body, outside any methods, creates a class attribute that is shared across all instances. Class attributes are stored in the class's __dict__ and are accessible via the class or any instance, unless shadowed by an instance attribute.

Exam trap

Python Institute often tests the distinction between class attributes and instance attributes, and the trap here is that candidates may confuse defining an attribute inside __init__ with creating a shared attribute, not realizing that __init__ always creates instance-specific attributes.

How to eliminate wrong answers

Option A is wrong because the @property decorator is used to define a method that can be accessed like an attribute, typically for computed or controlled access, not for creating a shared class attribute. Option B is wrong because defining an attribute inside a method without using self creates a local variable that is not accessible outside that method and does not become a class or instance attribute. Option D is wrong because defining the attribute inside __init__ using self creates an instance attribute, which is unique to each instance and not shared across all instances.

108
MCQhard

Which of the following is true regarding Python's method resolution order (MRO) in multiple inheritance?

A.The MRO is computed using the C3 linearization algorithm, ensuring that each class appears before its parents and that monotonicity is preserved.
B.The MRO is always the same as the order of base classes specified in the class statement.
C.The MRO can be changed at runtime by modifying the __bases__ attribute of a class.
D.The MRO is determined by the order of base classes in the class definition, using a depth-first, left-to-right search without consideration of diamond inheritance.
AnswerA

C3 linearization is the algorithm Python uses to compute the MRO at class creation time. It guarantees that every class appears before any of its parents in the linearization and that the local precedence order of bases is preserved across the entire hierarchy, a property called monotonicity. This ensures that `super()` calls follow a consistent, predictable order even in complex diamond inheritance, avoiding duplicate executions and inconsistent dispatch.

Why this answer

Python's method resolution order (MRO) is computed using the C3 linearization algorithm. This algorithm ensures that each class appears before its parents and that monotonicity is preserved, meaning the order of class precedence does not change when new subclasses are introduced. This is essential for resolving method calls in multiple inheritance scenarios, particularly with diamond inheritance.

Exam trap

The trap here is that candidates often assume the MRO follows a simple depth-first, left-to-right order (as in older Python versions or other languages), but Python's C3 algorithm can produce a different order to handle diamond inheritance correctly.

How to eliminate wrong answers

Option B is wrong because the MRO is not always the same as the order of base classes specified in the class statement; the C3 algorithm may reorder classes to satisfy monotonicity and local precedence order, especially in diamond inheritance. Option C is wrong because the MRO cannot be changed at runtime by modifying the __bases__ attribute; while __bases__ can be reassigned, the MRO is recalculated based on the new base classes using the C3 algorithm, but it is not directly mutable. Option D is wrong because the MRO is not determined by a simple depth-first, left-to-right search; Python explicitly abandoned that approach due to issues with diamond inheritance, and instead uses the C3 linearization algorithm to avoid inconsistent ordering.

109
MCQmedium

A team is using inheritance but wants to prevent a method from being overridden in subclasses. What Python feature can enforce this?

A.Use a private method with double underscore prefix (__method).
B.Use a @final decorator.
C.Use @staticmethod.
D.Use @abstractmethod.
AnswerB

Correct. The `@final` decorator from the `typing` module is designed to indicate that a method should not be overridden. Although not enforced at runtime, it is recognized by static type checkers and is the standard Python feature for this purpose.

Why this answer

The `@final` decorator, introduced in Python 3.8 via the `typing` module, is specifically intended to mark a method as final, signaling that it should not be overridden in subclasses. While Python does not enforce this at runtime, type checkers (e.g., mypy) can flag violations, making it the correct feature for preventing overriding in a design intent context. Option A, the double underscore prefix, only triggers name mangling (`_ClassName__method`), which is meant to avoid accidental collisions but does not prevent a subclass from defining a method with the mangled name directly, thus it does not enforce non-overridability.

Exam trap

Candidates often think that `@final` is unenforceable or not part of Python, but since Python 3.8 it is available in the `typing` module and is the standard way to declare a method final. Conversely, the double underscore prefix is frequently misconstrued as making a method impossible to override, but it is simply a naming convention that can be bypassed.

How to eliminate wrong answers

Option B is wrong because Python does not have a built-in `@final` decorator; it is not a standard Python feature (though it exists in some third-party libraries or type checkers like `typing.final` in Python 3.8+, but it is not enforced at runtime by the interpreter). Option C is wrong because `@staticmethod` defines a method that does not receive an implicit first argument (self or cls), but it does not prevent overriding in subclasses; a subclass can still define a method with the same name. Option D is wrong because `@abstractmethod` is used to declare a method as abstract, requiring subclasses to override it, which is the opposite of preventing overriding.

← PreviousPage 2 of 2 · 109 questions total

Ready to test yourself?

Try a timed practice session using only Object-Oriented Programming questions.