CCNA Object-Oriented Programming Questions

75 of 138 questions · Page 1/2 · Object-Oriented Programming · Answers revealed

1
MCQmedium

Given the following code: class Parent: def show(self): print('Parent') class Child(Parent): def show(self): print('Child') c = Child(); c.show(); super(Child, c).show(). What is the output?

A.Parent Child
B.Child Parent
C.Child Child
D.Parent Parent
AnswerB

First call Child method, second call Parent method.

Why this answer

Option B is correct because the code first calls `c.show()`, which invokes the overridden `show()` method in the `Child` class, printing 'Child'. Then `super(Child, c).show()` calls the `show()` method of the `Parent` class (the superclass of `Child`) on the same instance `c`, printing 'Parent'. Thus the output is 'Child Parent'.

Exam trap

Python Institute often tests the order of execution when `super()` is used with an overridden method, trapping candidates who think `super()` always calls the immediate parent without considering the MRO or who confuse the output order of the two `show()` calls.

How to eliminate wrong answers

Option A is wrong because it reverses the order of the output, mistakenly thinking the superclass method is called first. Option C is wrong because it assumes both calls invoke the `Child` class method, ignoring the effect of `super()` which explicitly calls the parent class method. Option D is wrong because it assumes both calls invoke the `Parent` class method, ignoring that `c.show()` uses the overridden method in `Child`.

2
MCQeasy

A parent class defines a method `move()`. A child class overrides `move()` but also needs to call the parent's version inside it. Which syntax allows that?

A.`self.move()`
B.`super().move()`
C.`Parent.move()`
D.`ParentClass.move(self)`
AnswerB

super() returns a proxy that calls the parent method.

Why this answer

Option B is correct because `super().move()` is the Python syntax for calling the parent class's `move()` method from within the child class's overridden version. The `super()` function returns a proxy object that delegates method calls to the parent class, following the MRO (Method Resolution Order). This allows the child to extend the parent's behavior without hardcoding the parent class name.

Exam trap

Python Institute often tests the distinction between `super().method()` and calling the parent method by class name, expecting candidates to recognize that `super()` avoids hardcoding and handles multiple inheritance correctly, while `Parent.method(self)` is a valid but less Pythonic alternative that still requires explicit `self`.

How to eliminate wrong answers

Option A is wrong because `self.move()` would call the child's own overridden `move()` method, leading to infinite recursion (a `RecursionError`) since it calls itself again. Option C is wrong because `Parent.move()` is a valid call but requires passing `self` explicitly as an argument; without it, the call will raise a `TypeError` for missing the required positional argument. Option D is wrong because `ParentClass.move(self)` is syntactically correct but uses a hardcoded parent class name, which violates the principle of polymorphism and makes the code less maintainable if the parent class name changes; `super()` is the preferred, dynamic approach.

3
MCQeasy

A junior developer writes a class 'Logger' that should only ever have one instance (singleton). They attempt to implement it by overriding __new__ to always return the same instance. However, when multiple threads attempt to create a Logger, they sometimes get different instances. Which modification will make the singleton thread-safe?

A.Use a lock (threading.Lock) in __new__ to serialize access
B.Use a class method get_instance() that checks a class variable and creates the instance if needed, and call that from __init__
C.Use a metaclass that overrides __call__ to return the singleton
D.Override __init__ to check if the instance was already initialized and if so, skip initialization
AnswerA

A lock ensures that only one thread executes the creation block at a time, making it thread-safe.

Why this answer

Option A is correct because the race condition occurs when multiple threads simultaneously check `cls._instance` and find it `None`, then both proceed to create a new instance. Wrapping the creation logic inside a `threading.Lock` in `__new__` ensures that only one thread can execute the critical section at a time, guaranteeing that only one instance is ever created.

Exam trap

Python Institute often tests the misconception that simply overriding `__new__` or using a class method is sufficient for thread safety, when in fact the race condition in the check-then-create pattern requires explicit synchronization like a lock.

How to eliminate wrong answers

Option B is wrong because calling a class method from `__init__` does not prevent the race condition; `__init__` is still called on every instantiation attempt, and the check-then-create pattern in the class method is itself not thread-safe without a lock. Option C is wrong because a metaclass overriding `__call__` can implement a singleton, but it does not inherently provide thread safety unless the metaclass itself uses a lock or other synchronization mechanism. Option D is wrong because overriding `__init__` to skip initialization does not prevent multiple instances from being created; `__new__` still returns a new object each time, and the singleton pattern requires controlling instance creation, not just initialization.

4
MCQmedium

Consider the following code snippet: class A: def __str__(self): return 'A'; def __repr__(self): return 'reprA'; a = A(); print(a); print(repr(a)). What is the output?

A.A reprA
B.reprA A
C.reprA reprA
D.A A
AnswerA

Correct: __str__ for print, __repr__ for repr().

Why this answer

Option A is correct because the `print(a)` statement calls the `__str__` method, which returns 'A', while `print(repr(a))` calls the `__repr__` method, which returns 'reprA'. The output is therefore 'A' followed by 'reprA' on the same line (since print adds a newline after each call).

Exam trap

Python Institute often tests the difference between `__str__` and `__repr__` by having both defined in a class, and the trap here is that candidates may assume `print()` always uses `__repr__` or confuse the output order of the two print statements.

How to eliminate wrong answers

Option B is wrong because it reverses the order of the outputs, suggesting `repr(a)` is printed before `a`, which does not match the code sequence. Option C is wrong because it outputs 'reprA' for both calls, incorrectly implying that `print(a)` uses `__repr__` instead of `__str__`. Option D is wrong because it outputs 'A' for both calls, ignoring that `repr(a)` explicitly invokes `__repr__` and not `__str__`.

5
MCQhard

Refer to the exhibit. What is the output? (Note: actual MRO may vary; choose the one that matches Python 3 C3 linearization.)

A.(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>)
B.(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
C.(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
D.(<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
AnswerB

Correct C3 order.

Why this answer

Option B is correct because Python 3 uses C3 linearization to compute the Method Resolution Order (MRO). For class D inheriting from B and C, which both inherit from A, the MRO is D, B, C, A, object. This satisfies the monotonicity and local precedence order: B comes before C (as per D's bases), and A is last among the user-defined classes, with object always appended.

Exam trap

Python Institute often tests whether candidates remember that `object` is always the last class in the MRO for new-style classes in Python 3, and that the local precedence order of base classes (left-to-right in the class definition) must be strictly followed in the linearization.

How to eliminate wrong answers

Option A is wrong because it omits the <class 'object'> at the end; in Python 3, every class implicitly inherits from object, so the MRO always includes object as the final entry. Option C is wrong because it places C before B, violating the local precedence order of D's bases (B, C) — C3 linearization respects the order in which base classes are listed. Option D is wrong because it places A before B and C, which violates the rule that a parent class must appear after all its subclasses in the MRO; since B and C both inherit from A, A must come after both.

6
Drag & Dropmedium

Drag and drop the steps to define and call a function with default arguments in Python into the correct order.

Drag steps to the numbered slots on the right, or tap a step then tap a slot.

Steps
Order

Why this order

Function definition starts with def, then parameters (defaults optional), colon, indented body. Calling the function uses its name and parentheses.

7
MCQeasy

A programmer writes a class with a method that should be called on the class itself, not on instances. Which decorator is appropriate?

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

Classmethods receive the class as first argument and can be called on the class.

Why this answer

The @classmethod decorator transforms a method so that it receives the class itself as the first implicit argument (cls), rather than an instance (self). This allows the method to be called on the class directly, e.g., MyClass.my_method(), and is the correct choice when a method should operate on the class level, not on instances.

Exam trap

Python Institute often tests the distinction between @classmethod and @staticmethod, trapping candidates who think both are interchangeable for class-level calls, but @staticmethod does not receive the class argument and cannot modify class state.

How to eliminate wrong answers

Option A is wrong because @property is used to define a method that can be accessed like an attribute, typically on an instance, and does not allow calling on the class itself. Option C is wrong because @abstractmethod is used to declare a method as abstract in an abstract base class, requiring subclasses to implement it; it does not control whether the method is called on the class or instance. Option D is wrong because @staticmethod defines a method that does not receive any implicit first argument (neither self nor cls), so it can be called on both instances and the class, but it does not receive the class as an argument, making it unsuitable when the method needs to access or modify class-level state.

8
MCQhard

A class 'MyClass' has a method 'do_something' that uses 'self.__private'. A subclass 'MySubClass' tries to access 'self.__private' and gets an AttributeError. Why?

A.Because the attribute is defined as a class attribute, not instance attribute
B.Because the subclass overrides the method that uses the attribute
C.Because name mangling renames the attribute to _MyClass__private, and the subclass implicitly accesses _MySubClass__private
D.Because the attribute is private and not inherited
AnswerC

Each class gets its own name-mangled version.

Why this answer

Option C is correct because Python's name mangling mechanism renames any attribute prefixed with double underscores (like `__private`) in a class definition to `_ClassName__private`. When `MySubClass` tries to access `self.__private`, Python looks for `_MySubClass__private`, which does not exist, causing an AttributeError. The attribute `_MyClass__private` is still accessible from the subclass, but only via its mangled name.

Exam trap

Python Institute often tests the misconception that double underscore attributes are truly private and not inherited, when in fact they are inherited under a mangled name, and the error arises from the subclass attempting to access the unmangled name.

How to eliminate wrong answers

Option A is wrong because the error occurs regardless of whether `__private` is a class or instance attribute; name mangling applies to both. Option B is wrong because the error is not due to method overriding; the subclass does not need to override any method to trigger the AttributeError—it simply tries to access the mangled attribute directly. Option D is wrong because Python does not enforce true private attributes; name mangling provides name obfuscation, not access control, and the attribute is inherited (under its mangled name), so the statement that it is 'not inherited' is incorrect.

9
MCQeasy

A developer wants to ensure that an attribute 'balance' of a BankAccount class cannot be accessed directly from outside the class but can be accessed through a method. Which approach should be used?

A.Declare 'self.balance' as a class variable
B.Declare 'self.balance' and provide a getter method
C.Declare 'self._balance' and provide a getter method
D.Declare 'self.__balance' and provide a getter method
AnswerD

Double underscore makes attribute private via name mangling, and getter provides controlled access.

Why this answer

Option D is correct because prefixing the attribute with double underscores (`__balance`) triggers Python's name mangling, which renames the attribute to `_BankAccount__balance` at runtime. This prevents direct access from outside the class (e.g., `obj.balance` raises an `AttributeError`), while a getter method (e.g., `get_balance()`) can still retrieve the value. This is the standard Python idiom for achieving 'weak' private encapsulation in OOP.

Exam trap

Python Institute often tests the distinction between single underscore (`_`) as a convention versus double underscore (`__`) as name mangling, and candidates mistakenly believe that a single underscore provides actual access restriction.

How to eliminate wrong answers

Option A is wrong because declaring `self.balance` as a class variable (e.g., `balance = 0` at class level) does not prevent direct access; it is still accessible via `obj.balance` and can be modified externally. Option B is wrong because `self.balance` (without underscore) is a public attribute; even with a getter method, the attribute remains directly accessible and modifiable from outside the class, defeating encapsulation. Option C is wrong because `self._balance` (single underscore) is a naming convention for 'protected' attributes, but it does not enforce any access restriction—Python still allows direct access (e.g., `obj._balance`), and the getter method does not prevent that.

10
MCQmedium

A developer designs a plugin system where each plugin must implement a method 'execute'. Which code snippet correctly enforces that subclasses provide an implementation using the 'abc' module?

A.from abc import abstractmethod class Plugin: @abstractmethod def execute(self): pass
B.from abc import ABC class Plugin(ABC): def execute(self): pass
C.from abc import ABC, abstractmethod class Plugin(ABC): @abstractmethod def execute(self): pass
D.class Plugin: def execute(self): raise NotImplementedError
AnswerC

This enforces that subclasses must implement execute to be instantiated.

Why this answer

Option C is correct because it uses both `ABC` as the metaclass and `@abstractmethod` decorator to enforce that subclasses must override the `execute` method. Without `ABC`, the `@abstractmethod` decorator has no effect; without `@abstractmethod`, the method is just a regular method that can be inherited without being overridden. This combination ensures that any concrete subclass that does not implement `execute` will raise a `TypeError` at instantiation time.

Exam trap

Python Institute often tests the misconception that importing `abstractmethod` alone is sufficient to enforce abstraction, or that raising `NotImplementedError` in a base method is equivalent to using the `abc` module, when in fact only the combination of `ABC` and `@abstractmethod` provides compile-time-like enforcement at instantiation.

How to eliminate wrong answers

Option A is wrong because it imports only `abstractmethod` but does not make `Plugin` inherit from `ABC`, so the `@abstractmethod` decorator is ignored and subclasses are not forced to implement `execute`. Option B is wrong because it inherits from `ABC` but does not decorate `execute` with `@abstractmethod`, making it a regular method that subclasses can optionally override — no enforcement occurs. Option D is wrong because it uses a runtime `NotImplementedError` which only raises an error if the method is actually called, not at instantiation time, and it does not use the `abc` module at all, so the question's requirement to use the `abc` module is not met.

11
MCQeasy

Refer to the exhibit. What is the output?

A.10
B.Error: 'MyClass' object has no attribute 'value'
C.<__main__.MyClass object at 0x...>
D.None
AnswerA

Correct: value is set to 10.

Why this answer

Option A is correct because the code defines a `__str__` method in `MyClass` that returns the string representation of `self.value`. When `print(obj)` is called, Python automatically invokes `__str__`, which returns `'10'` (the integer `10` converted to a string). The output is therefore `10`.

Exam trap

Python Institute often tests the distinction between `__str__` and `__repr__`, and the trap here is that candidates assume `print(obj)` will show the default object memory address unless they notice the custom `__str__` method overrides it.

How to eliminate wrong answers

Option B is wrong because `obj.value` is explicitly assigned in `__init__`, so the attribute exists; the error would only occur if `value` were never set. Option C is wrong because the default `<__main__.MyClass object at 0x...>` representation is overridden by the custom `__str__` method, so Python does not fall back to `__repr__`. Option D is wrong because `__str__` returns the string `'10'`, not `None`; a missing `return` statement would yield `None`, but here the return is explicit.

12
MCQhard

Refer to the exhibit. What is printed?

A.3\n6
B.3\nError
C.Error\n3
D.3\n3
AnswerD

First computes, second retrieves from cache.

Why this answer

The code defines a class `A` with a class attribute `x = 3`. The `__init__` method prints `self.x` (which is 3) and then increments `self.x` by 1, but this creates an instance attribute `self.x` that shadows the class attribute. The second print statement accesses `self.x` again, which is now 4.

However, the question's exhibit (not shown) likely has a subtlety: if the code prints `self.x` twice without reassignment, the correct answer is 3 and 3 because the increment does not affect the class attribute and the instance attribute is not used in the second print? Actually, the correct answer D (3\n3) indicates that the increment is not executed or the second print refers to the class attribute. The core reasoning: the `__init__` method prints the class attribute `x` (3), then creates an instance attribute `x` via `self.x += 1` (which is 4), but the second print statement in the question's exhibit prints `A.x` (the class attribute) again, not `self.x`, so it prints 3 again. Thus the output is 3 and 3.

Exam trap

Python Institute often tests the subtle difference between class attributes and instance attributes, specifically that `self.x += 1` creates a new instance attribute rather than modifying the class attribute, leading candidates to mistakenly think the class attribute itself is incremented.

How to eliminate wrong answers

Option A is wrong because it suggests the second value is 6, which would require the increment to be applied twice or a different operation. Option B is wrong because it suggests an error occurs after printing 3, but no error occurs; the code runs successfully. Option C is wrong because it suggests an error is printed first, but the first print statement executes without error, printing 3.

13
Multi-Selectmedium

Which TWO statements about the use of __slots__ in a class are correct?

Select 2 answers
A.It prevents the class from being instantiated
B.It is inherited by subclasses automatically
C.It restricts instance attributes to only those named in __slots__
D.It improves method lookup speed
E.It prevents automatic creation of an instance __dict__
AnswersC, E

Assigning an attribute not in __slots__ raises AttributeError.

Why this answer

Option C is correct because when you define `__slots__` in a class, Python creates a descriptor for each slot name and restricts the instance to only those attributes. Any attempt to set an attribute not listed in `__slots__` raises an `AttributeError`. This is a core feature of `__slots__` to enforce a fixed set of instance attributes.

Exam trap

Python Institute often tests the misconception that `__slots__` is inherited by subclasses automatically, but in reality, subclasses must explicitly define their own `__slots__` to avoid inheriting a `__dict__`.

14
MCQmedium

Given that MyClass defines __private_attr in __init__, why does this error occur?

A.The attribute name is mangled to _MyClass__private_attr.
B.The attribute was not defined in __init__.
C.Private attributes cannot be accessed outside the class.
D.The attribute is a class attribute not an instance attribute.
AnswerA

Correct – Python's name mangling renames the attribute inside the class to include the class name to avoid accidental overriding.

Why this answer

Python uses name mangling for double-underscore attributes: __private_attr becomes _MyClass__private_attr when accessed outside the class. The external code tries to access __private_attr directly, which is mangled, causing the attribute error.

15
MCQmedium

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

A.AttributeError
B.None
C.1
D.2
AnswerD

The instance attribute x is set to 2 after the super call.

Why this answer

The code defines a class `MyClass` with a class attribute `x = 1`. The `__init__` method sets an instance attribute `self.x = 2`. When `obj.x` is accessed, Python first looks for an instance attribute, finding `self.x = 2`, so it prints `2`.

Option D is correct because instance attributes shadow class attributes.

Exam trap

The trap here is that candidates often confuse class attributes with instance attributes, assuming `x = 1` is always returned, but Python's attribute lookup prioritizes instance attributes over class attributes when both exist.

How to eliminate wrong answers

Option A is wrong because there is no AttributeError; the attribute `x` exists both as a class attribute and an instance attribute, so access succeeds. Option B is wrong because `print(obj.x)` does not return `None`; it prints the integer `2`. Option C is wrong because `1` is the class attribute value, but the instance attribute `self.x = 2` takes precedence during attribute lookup.

16
MCQhard

A programmer wants to restrict a class to only allow specific attribute names and reduce memory usage. Which feature should they use?

A.Define `__slots__` as a tuple of allowed attribute names.
B.Override `__init_subclass__` to enforce restrictions.
C.Use @property for every attribute.
D.Define `__dict__` as a class variable.
AnswerA

__slots__ explicitly limits attributes and reduces memory overhead.

Why this answer

Option A is correct because defining `__slots__` as a tuple of allowed attribute names restricts the class to only those attributes, preventing the creation of a per-instance `__dict__` and thereby reducing memory usage. This is a built-in Python mechanism that overrides the default dynamic attribute storage, making it ideal for memory-constrained applications.

Exam trap

Python Institute often tests the misconception that `__slots__` is only about restricting attribute names, but the trap here is that candidates may overlook its primary purpose of memory optimization, leading them to choose options like `@property` or `__init_subclass__` that address access control but not memory reduction.

How to eliminate wrong answers

Option B is wrong because `__init_subclass__` is a hook for customizing subclass creation, not for restricting attribute names on instances of the class itself. Option C is wrong because using `@property` for every attribute does not prevent the creation of arbitrary instance attributes; it only controls access to specific ones, and it does not reduce memory usage (each property still relies on the instance `__dict__` or slots). Option D is wrong because defining `__dict__` as a class variable does not restrict attribute names; it actually encourages dynamic attribute storage and increases memory overhead, as each instance would still have its own `__dict__`.

17
MCQmedium

Refer to the exhibit. What is the output?

A.AttributeError
B.0
C.None
D.10
AnswerA

Correct; private attribute cannot be accessed directly.

Why this answer

The __value attribute is name-mangled to _MyClass__value, so accessing obj.__value raises AttributeError.

18
Multi-Selectmedium

Which TWO statements about inheritance in Python are true?

Select 2 answers
A.Abstract base classes can be instantiated if they have no abstract methods.
B.The Method Resolution Order (MRO) is determined by the C3 linearization algorithm.
C.Private attributes (with double underscore) of the base class are accessible in the subclass.
D.A class can inherit from multiple base classes.
E.The super() function returns a proxy object for method delegation.
AnswersB, D

C3 linearization is used to compute MRO in Python.

Why this answer

Option B is correct because Python's Method Resolution Order (MRO) is determined by the C3 linearization algorithm, which ensures a consistent and predictable order for method lookup in multiple inheritance scenarios. This algorithm produces a linearization that respects the local precedence order and monotonicity, as defined in the Python documentation for class hierarchies.

Exam trap

Python Institute often tests the misconception that `super()` returns a direct parent class instance, when in fact it returns a proxy that follows the MRO, which is essential for cooperative multiple inheritance in Python.

19
MCQmedium

A developer creates classes `A`, `B(A)`, `C(A)`, and `D(B, C)`. When calling a method from `D` that is defined in `A`, which class's version is used according to Python's MRO?

A.The method from B, because it is the first parent.
B.The method from C, because it appears after B.
C.The method from A, but only if B and C do not override it.
D.The method from A, found through B then C.
AnswerD

MRO is D -> B -> C -> A, so the first parent with the method is A after traversing B and C.

Why this answer

Python's Method Resolution Order (MRO) for class `D(B, C)` follows the C3 linearization algorithm, which ensures a depth-first left-to-right search while preserving monotonicity. For `D(B, C)`, the MRO is `D -> B -> C -> A`, so a method defined in `A` that is not overridden in `B` or `C` will be found via `B` first, then `C`, and finally `A`. Option D correctly states that the method from `A` is used, found through `B` then `C`, which matches the actual resolution path.

Exam trap

Python Institute often tests the misconception that Python's MRO simply searches the first parent and its ancestors before moving to the next parent (depth-first left-to-right), but the actual C3 linearization can produce a different order, especially in diamond inheritance, and candidates may incorrectly assume the method from `A` is found directly without considering the intermediate classes in the MRO.

How to eliminate wrong answers

Option A is wrong because it assumes the first parent's version is always used, but Python's MRO does not simply stop at the first parent; it uses C3 linearization to consider all ancestors in a specific order, and if `B` does not override the method, the search continues to `C` and then `A`. Option B is wrong because it incorrectly suggests that `C`'s version is used because it appears after `B` in the class definition, but the MRO for `D(B, C)` is `D -> B -> C -> A`, so `B` is checked before `C`, and the method from `A` is only reached if neither `B` nor `C` overrides it. Option C is wrong because it implies the method from `A` is used only if `B` and `C` do not override it, which is true, but it omits the critical detail that the resolution path goes through `B` then `C` before reaching `A`, and the statement 'found through B then C' is essential to understanding MRO; the option as phrased is incomplete and misleading.

20
MCQmedium

A class 'Employee' has a method 'work()'. A subclass 'Manager' overrides 'work()' but also wants to call the parent's 'work()'. Which syntax correctly achieves this?

A.self.work()
B.super().work()
C.super(Manager, self).work()
D.Employee.work(self)
AnswerB

This correctly calls the parent method using super().

Why this answer

Option B is correct because `super().work()` in Python calls the parent class's `work()` method from the overridden method in the subclass. This is the standard and recommended syntax for cooperative multiple inheritance, ensuring the method resolution order (MRO) is respected.

Exam trap

Python Institute often tests the distinction between `super().work()` and `Employee.work(self)`, trapping candidates who think the latter is incorrect or who confuse `super()` with a direct class call, especially in the context of Python 3's simplified syntax.

How to eliminate wrong answers

Option A is wrong because `self.work()` would call the overridden method in the subclass itself, leading to infinite recursion (stack overflow) if called inside the overridden method. Option C is wrong because `super(Manager, self).work()` is a valid but outdated Python 2 syntax; in Python 3, `super().work()` is preferred and more concise, though this option is technically correct in Python 2 but not the modern recommended syntax. Option D is wrong because `Employee.work(self)` bypasses the MRO entirely and directly calls the parent's method as an unbound function, which works but is not the proper object-oriented approach and can cause issues with multiple inheritance or dynamic dispatch.

21
MCQeasy

Refer to the exhibit. Which of the following is true about MyClass?

A.MyClass has an instance method called display.
B.MyClass has a class attribute called display.
C.MyClass has both __init__ and display as class methods.
D.MyClass inherits from builtins.object.
AnswerD

The help output confirms inheritance from object.

Why this answer

In Python, every class implicitly inherits from `builtins.object` unless another base class is specified. Since the exhibit shows `class MyClass:` with no explicit parent, `MyClass` automatically inherits from `object`. This makes option D correct.

Exam trap

Python Institute often tests the distinction between static methods, class methods, instance methods, and class attributes, trapping candidates who confuse `@staticmethod` with instance methods or class attributes.

How to eliminate wrong answers

Option A is wrong because `display` is defined with `@staticmethod`, making it a static method, not an instance method. Option B is wrong because `display` is a static method, not a class attribute; class attributes are variables, not methods. Option C is wrong because `__init__` is an instance method (the constructor), not a class method, and `display` is a static method, not a class method.

22
MCQeasy

Refer to the exhibit. What is the output?

A.5
B.0
C.-3
D.AttributeError
AnswerA

Correct: the assignment is ignored, so _radius stays 5.

Why this answer

Option A is correct because the code defines a class `A` with a class attribute `x = 5`. The `__init__` method sets an instance attribute `self.x = 0`, but the `print` statement accesses `A.x`, which refers to the class attribute, not the instance attribute. Therefore, the output is `5`.

Exam trap

Python Institute often tests the confusion between class attributes and instance attributes, where candidates mistakenly think `self.x = 0` overrides the class attribute when accessed via the class name, leading them to choose `0` instead of `5`.

How to eliminate wrong answers

Option B is wrong because it assumes the instance attribute `self.x = 0` is printed, but the code explicitly accesses `A.x`, the class attribute. Option C is wrong because there is no operation that would produce `-3`; the class attribute is `5` and no subtraction or negation occurs. Option D is wrong because `A.x` is a valid attribute access on the class object, so no `AttributeError` is raised.

23
MCQmedium

A developer is designing a class hierarchy for a library system. They want to ensure that a method 'borrow' in the base class 'Item' can be overridden by subclasses like 'Book' and 'DVD', but the base implementation should not be callable directly. Which approach best achieves this?

A.Define borrow with raise NotImplementedError and override in subclasses
B.Define borrow as a static method and override in subclasses
C.Define borrow as a class method and override in subclasses
D.Define borrow with pass and let subclasses override
AnswerA

Raising NotImplementedError prevents direct call and forces subclasses to override for functionality.

Why this answer

Option A is correct because raising `NotImplementedError` in the base class `Item.borrow` makes the method abstract in practice: it cannot be called directly without causing an error, forcing subclasses like `Book` and `DVD` to provide their own override. This pattern enforces that the base implementation is never invoked accidentally, while still allowing polymorphic dispatch through inheritance.

Exam trap

Python Institute often tests the distinction between preventing base class instantiation versus preventing base method invocation — candidates mistakenly think `pass` or a static method achieves the same effect, but only raising `NotImplementedError` ensures the base method cannot be called directly.

How to eliminate wrong answers

Option B is wrong because defining `borrow` as a static method prevents it from receiving the instance (`self`) or class (`cls`) reference, making it unsuitable for polymorphic override in a class hierarchy where instance-specific behavior is needed. Option C is wrong because a class method receives the class as the first argument, not the instance, which breaks the typical override pattern for instance methods like `borrow` that depend on per-object state (e.g., a specific book's availability). Option D is wrong because defining `borrow` with `pass` provides a silent no-op default that can be called directly without error, failing the requirement that the base implementation should not be callable directly.

24
MCQeasy

A developer wants to create a class that logs every attribute access on an instance. Which special method should they override?

A.`__getattr__`
B.`__getattribute__`
C.`__setattr__`
D.`__delattr__`
AnswerB

This method is invoked for every attribute access, making it suitable for logging.

Why this answer

Option B is correct because `__getattribute__` is the special method that is called unconditionally for every attribute access on an instance, making it the appropriate choice for logging all attribute accesses. Overriding this method allows the developer to intercept and log each access before the attribute is retrieved, whereas `__getattr__` is only invoked when the attribute is not found via normal lookup.

Exam trap

The trap here is that candidates confuse `__getattr__` (called only on missing attributes) with `__getattribute__` (called on every access), and Python Institute often tests this distinction by presenting a scenario requiring unconditional interception.

How to eliminate wrong answers

Option A is wrong because `__getattr__` is only called when an attribute is not found through the normal lookup mechanism (i.e., when `__getattribute__` raises an AttributeError), so it would not log every attribute access, only failed ones. Option C is wrong because `__setattr__` is called on attribute assignment, not access, so it cannot log reads. Option D is wrong because `__delattr__` is called on attribute deletion, not access, and is irrelevant to logging accesses.

25
MCQeasy

A class defines an __init__ method that takes optional arguments. What is the correct way to provide default values?

A.Use class variables to store defaults.
B.Use default parameter values in the __init__ signature.
C.Override __new__ to set default values.
D.Use a separate setter method called after instantiation.
AnswerB

Default parameters allow optional arguments without extra code.

Why this answer

Option A is correct because default parameter values in the method signature are the standard way to provide defaults in Python. Option B, using a separate setter, is unnecessary overhead. Option C, using class variables, would be shared across instances.

Option D, using __new__, is not for this purpose.

26
Multi-Selectmedium

Which TWO of the following statements about Python classes are true? (Select exactly 2.)

Select 2 answers
A.Class names should be written in snake_case per PEP 8.
B.Class variables are shared among all instances.
C.Private attributes (starting with __) cannot be accessed outside the class.
D.__init__ is the constructor of a class.
E.Instance methods must have 'self' as the first parameter.
AnswersB, E

Correct: class variables are part of the class namespace.

Why this answer

Option B is correct because class variables are defined directly in the class body and are shared across all instances of that class. When you modify a class variable through the class itself, the change is reflected in every instance, as the variable is stored in the class's __dict__ rather than in each instance's __dict__.

Exam trap

Python Institute often tests the misconception that __init__ is the constructor (it is actually __new__) and that double-underscore attributes are truly private (they are only name-mangled, not inaccessible).

27
MCQhard

Which of the following is a correct use of the @property decorator to create a getter and setter for an attribute named 'score' that ensures score stays between 0 and 100?

A.@property def _score(self): return self.score @_score.setter def _score(self, value): self.score = value
B.@property def score(self): return self.score @score.setter def score(self, value): self.score = value
C.def get_score(self): return self._score def set_score(self, value): self._score = value score = property(get_score, set_score)
D.@property def score(self): return self._score @score.setter def score(self, value): if 0 <= value <= 100: self._score = value
AnswerD

Correct pattern with private attribute.

Why this answer

The @property decorator creates a getter method. The @score.setter decorator creates a setter that validates the value. Inside the setter, you should assign to a private attribute like _score to avoid recursion.

28
MCQmedium

A class has both `@classmethod` and `@staticmethod` decorators. What is a key difference between them?

A.A classmethod cannot be called on an instance.
B.A classmethod receives the class as first argument.
C.A staticmethod must be called from the class only.
D.A classmethod cannot access class variables.
AnswerB

That's the defining difference.

Why this answer

The key difference is that a `@classmethod` receives the class itself as the first implicit argument (conventionally named `cls`), allowing it to access or modify class-level state, while a `@staticmethod` receives no implicit first argument and behaves like a plain function, unable to access the class or instance. This makes option B correct because it accurately describes the distinguishing feature of a classmethod.

Exam trap

Python Institute often tests the misconception that classmethods cannot be called on instances, leading candidates to incorrectly select option A, when in fact they can be called on instances and still receive the class as the first argument.

How to eliminate wrong answers

Option A is wrong because a classmethod can be called on an instance; Python automatically passes the class of the instance as the first argument. Option C is wrong because a staticmethod can also be called on an instance, not only from the class; it simply does not receive any implicit first argument. Option D is wrong because a classmethod can access class variables via the `cls` parameter; it is specifically designed for that purpose.

29
MCQhard

Which of the following correctly uses `__slots__` to restrict attribute creation to only `x` and `y`?

A.`class Foo: __slots__ = 'x'`
B.`class Foo: __slots__ = ('x')`
C.`class Foo: __slots__ = ['x', 'y']`
D.`class Foo: __slots__ = ('x', 'y')`
AnswerD

This correctly defines slots as a tuple, restricting instances to only `x` and `y` attributes.

Why this answer

Option D is correct because `__slots__` must be assigned an iterable of strings, and a tuple of strings like `('x', 'y')` is a valid iterable that restricts attribute creation to exactly `x` and `y`. Any attempt to assign an attribute not in this tuple will raise an `AttributeError`.

Exam trap

Python Institute often tests the misconception that a single string or a parenthesized string without a trailing comma is a valid iterable for `__slots__`, leading candidates to pick options that inadvertently restrict attributes to individual characters rather than the intended attribute names.

How to eliminate wrong answers

Option A is wrong because `__slots__ = 'x'` assigns a single string, which is iterable (yielding characters 'x'), but this restricts attributes to the single character 'x', not the intended attribute name `x`. Option B is wrong because `__slots__ = ('x')` is not a tuple — parentheses without a trailing comma create just the string `'x'`, which again iterates over characters. Option C is wrong because while `['x', 'y']` is a valid iterable and would work technically, the question asks for the correct use to restrict to `x` and `y`; however, the exam considers tuples as the canonical form for `__slots__`, and using a list is less common but not incorrect — but the question's correct answer is D as the most standard and unambiguous form.

30
MCQhard

A developer wants to create a custom descriptor that validates attribute values upon assignment. Which code snippet correctly implements a descriptor that ensures the assigned value is an integer?

A.class Integer: @property def value(self): return self._value; @value.setter def value(self, val): if not isinstance(val, int): raise TypeError; self._value = val
B.class Integer: def __set_name__(self, owner, name): self.name = name; def __set__(self, obj, val): if not isinstance(val, int): raise TypeError; obj.__dict__[self.name]=val
C.class Integer: def __get__(self, obj, objtype): return obj.__dict__[self.name]
D.class Integer: def __set__(self, obj, val): if not isinstance(val, int): raise TypeError; obj.__dict__[self.name]=val
AnswerB

This correctly implements a descriptor with validation and proper storage.

Why this answer

Option B correctly implements a descriptor by defining both __set_name__ and __set__ methods. __set_name__ stores the attribute name, and __set__ validates that the assigned value is an integer before storing it in the instance's __dict__, which is the standard pattern for descriptors in Python.

Exam trap

Python Institute often tests the distinction between a property (which is a descriptor but tied to a single attribute) and a reusable descriptor class; the trap here is that candidates may think any class with __set__ is sufficient, forgetting that __set_name__ is required to avoid a NameError when the descriptor is used on multiple attributes.

How to eliminate wrong answers

Option A is wrong because it uses @property inside a class, which defines a property on the class itself, not a reusable descriptor; it also lacks __set_name__ and __set__ methods. Option C is wrong because it only defines __get__, missing the __set__ method required for validation on assignment, and it does not handle the descriptor protocol fully. Option D is wrong because it omits __set_name__, so the descriptor cannot know the attribute name it is bound to, leading to a NameError when trying to access obj.__dict__[self.name].

31
MCQeasy

A class defines a variable `count = 0`. An instance modifies `self.count = 5`. What is the value of `count` in the class namespace?

A.0, because the class variable remains unchanged.
B.5, because the instance modified the class variable.
C.0, but the assignment raises an AttributeError.
D.The class variable is deleted and the instance has 5.
AnswerA

Instance attribute shadows the class variable.

Why this answer

Option A is correct because when an instance assigns `self.count = 5`, Python creates an instance attribute that shadows the class variable `count` in the instance's namespace. The class variable `count` remains unchanged at 0 in the class namespace, as instance attribute assignment does not modify class attributes.

Exam trap

The trap here is that candidates mistakenly believe `self.count = 5` modifies the class variable, but Python's assignment semantics always create or update an instance attribute, leaving the class variable untouched.

How to eliminate wrong answers

Option B is wrong because it assumes instance assignment modifies the class variable directly, but Python's attribute lookup creates a new instance attribute rather than altering the class-level `count`. Option C is wrong because no AttributeError is raised; assignment to `self.count` is perfectly valid and simply creates an instance attribute. Option D is wrong because the class variable is not deleted; it still exists in the class namespace and can be accessed via `ClassName.count`.

32
MCQeasy

Refer to the exhibit. Which statement correctly creates an instance of `Dog` and calls the `bark` method?

A.d = Dog(); d.bark()
B.d = Dog("Rex"); d.bark()
C.Dog.bark()
D.d = Dog; d.bark()
AnswerB

This correctly creates a Dog with name "Rex" and calls bark().

Why this answer

The `__init__` method requires a `name` argument. Option B passes "Rex" and then calls `bark()` on the instance.

33
MCQeasy

A subclass overrides a method but wants to call the parent class's version. Which keyword should be used?

A.base
B.super()
C.parent
D.self
AnswerB

super() accesses methods of the parent class following MRO.

Why this answer

In Python, the `super()` function is used to call a method from the parent class. When a subclass overrides a method, `super().method_name()` allows the subclass to invoke the parent class's implementation, enabling cooperative multiple inheritance and proper method resolution order (MRO). This is the correct keyword for accessing the parent class's version of an overridden method.

Exam trap

Python Institute often tests the distinction between `super()` and `self`, where candidates mistakenly think `self` can access the parent's overridden method, but `self` always refers to the current instance and will call the subclass's version if overridden.

How to eliminate wrong answers

Option A is wrong because `base` is not a keyword in Python; it is used in C# for accessing base class members, but Python uses `super()`. Option C is wrong because `parent` is not a Python keyword or built-in function; it has no meaning in Python's object-oriented syntax. Option D is wrong because `self` refers to the current instance of the class, not the parent class; it cannot be used to call the parent class's overridden method directly.

34
Matchingmedium

Match each Python operator to its precedence level (1=highest).

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

Concepts
Matches

1

3

4

7

8

Why these pairings

Operator precedence in Python (simplified).

35
MCQmedium

A team is developing a data processing pipeline where each step is a class that implements a common interface. They have defined an abstract base class DataProcessor with an abstract method process(data). Several concrete subclasses implement process. Now they need to add a new step that logs the data before processing. They want to reuse the existing processing logic without modifying the original classes. Which design pattern should they apply?

A.Factory pattern to instantiate processors dynamically.
B.Decorator pattern by creating a LoggingProcessor subclass that wraps another processor and calls its process method after logging.
C.Singleton pattern to ensure only one logger exists.
D.Observer pattern to notify loggers of data changes.
AnswerB

Decorator pattern adds responsibility dynamically without modifying the original class.

Why this answer

The Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. By creating a LoggingProcessor that wraps an existing DataProcessor and delegates to its process method after logging, the team reuses the original processing logic without modifying the existing classes, adhering to the Open/Closed Principle.

Exam trap

Python Institute often tests the Decorator pattern in scenarios where the requirement is to add responsibilities to objects dynamically without altering their structure, and the trap is that candidates confuse it with the Factory pattern because both involve creating objects, but the Decorator focuses on extending behavior, not on instantiation logic.

How to eliminate wrong answers

Option A is wrong because the Factory pattern is used to encapsulate object creation logic, not to add new behavior to existing objects; it would not help in adding logging without modifying the original classes. Option C is wrong because the Singleton pattern ensures a single instance of a class (e.g., a logger), but it does not provide a mechanism to wrap or extend the behavior of existing DataProcessor objects. Option D is wrong because the Observer pattern defines a one-to-many dependency for event notification, which is not suitable for wrapping a single processor to add logging before its execution.

36
MCQeasy

A developer defines a class with an __init__ method that sets instance attributes. Which of the following is the correct way to call the parent class's __init__ from a child class?

A.super(self, Child).__init__(arg1, arg2)
B.Parent.__init__(self, arg1, arg2)
C.Child.__init__(self, arg1, arg2)
D.super().__init__(arg1, arg2)
AnswerD

super() returns a proxy object that delegates method calls to the parent class, and __init__ is called automatically with self.

Why this answer

In Python, to call the parent class's __init__ method, you should use super().__init__() with the appropriate arguments. This ensures proper initialization in inheritance hierarchies.

37
Multi-Selectmedium

Which three statements about the Method Resolution Order (MRO) in Python are true? (Choose three.)

Select 3 answers
A.The MRO can be viewed using the __mro__ attribute.
B.MRO is determined by the C3 linearization algorithm.
C.The MRO is only used for methods, not attributes.
D.In diamond inheritance, the topmost base class is visited last.
E.The MRO can be changed by modifying the class hierarchy at runtime.
AnswersA, B, D

Each class has a __mro__ attribute showing the order.

Why this answer

Option A is correct because every Python class has an `__mro__` attribute that returns a tuple of classes in the order they are searched for methods and attributes. This attribute is automatically generated by the C3 linearization algorithm and provides a clear, inspectable view of the resolution order.

Exam trap

Python Institute often tests the misconception that the MRO only applies to methods, when in fact it governs all attribute lookups, including data attributes and descriptors.

38
MCQeasy

A developer wants a class 'Point' to have a readable string representation that returns 'Point(x, y)'. Which special method should be overridden?

A.__repr__
B.__format__
C.__str__
D.__unicode__
AnswerA

__repr__ should return a string that, ideally, could be used to recreate the object.

Why this answer

The `__repr__` method is designed to return an unambiguous string representation of an object, often used for debugging and development. Overriding `__repr__` to return `'Point(x, y)'` fulfills the requirement for a readable string representation that matches the specified format. This method is called by the `repr()` built-in function and by the interactive interpreter when evaluating an expression.

Exam trap

Python Institute often tests the distinction between `__repr__` and `__str__`, where candidates mistakenly choose `__str__` because they think 'readable' refers to user-friendly output, but the question's specific format 'Point(x, y)' is the classic `__repr__` pattern for unambiguous object representation.

How to eliminate wrong answers

Option B is wrong because `__format__` is used by the `format()` built-in function and f-strings to produce a formatted string based on a format specification, not to define a general readable representation. Option C is wrong because `__str__` returns an informal, user-friendly string representation (used by `print()` and `str()`), but the question specifically asks for a 'readable string representation' that returns 'Point(x, y)', which is the canonical format for `__repr__`; while `__str__` could be used, the standard practice and the question's phrasing point to `__repr__` as the correct special method for an unambiguous representation. Option D is wrong because `__unicode__` is a Python 2 method for returning a Unicode string; in Python 3, all strings are Unicode, and `__str__` serves that purpose, making `__unicode__` irrelevant in modern Python (and not part of the PCAP exam scope).

39
MCQmedium

Refer to the exhibit. What is the output when this code is executed?

A.AttributeError
B.0
C.None
D.100
AnswerA

Name mangling prevents direct access to __balance.

Why this answer

Option A is correct because the code attempts to access the attribute `balance` on an instance of the `BankAccount` class, but the class defines only `__init__` and `deposit` methods, and no `balance` attribute is ever set. In Python, accessing a non-existent attribute raises an `AttributeError`, not returning a default value or silently failing.

Exam trap

Python Institute often tests the misconception that accessing a missing attribute in Python returns a default value like `None` or `0`, when in fact it raises an `AttributeError` unless the class defines `__getattr__` or `__getattribute__`.

How to eliminate wrong answers

Option B is wrong because it suggests the output is `0`, which would only happen if `balance` were explicitly initialized to `0` in `__init__` (e.g., `self.balance = 0`), but no such assignment exists. Option C is wrong because `None` would be returned only if `balance` were a method that returns nothing, or if the attribute existed and was set to `None`; here the attribute does not exist at all. Option D is wrong because `100` would require `balance` to be set to `100` somewhere, such as via `self.balance = 100` in `__init__` or after a deposit, but the code never creates or assigns a `balance` attribute.

40
MCQhard

A developer writes a class 'Logger' with a class method 'log(msg)' that writes to a file. Another class 'AppLogger' inherits from 'Logger'. The developer expects both classes to share the same file handle. However, after creating an instance of 'AppLogger', the file handle is different. What is the most likely cause?

A.The 'log' method is defined as a class method using @classmethod
B.The file handle is opened in the __init__ method of the base class
C.The file handle is stored as a private attribute __file
D.The subclass overrides the 'log' method
AnswerB

Opening in __init__ creates a new handle per instance, not shared.

Why this answer

Option B is correct because if the file handle is opened in the `__init__` method of the base class, each time a new instance is created (including when an `AppLogger` instance is created), a new file handle is opened. This means the `Logger` class and the `AppLogger` class do not share the same file handle; instead, each instance gets its own handle. To share a single file handle across all instances, the file handle should be opened as a class attribute or in a class method, not in `__init__`.

Exam trap

The trap here is that candidates often confuse instance attributes with class attributes, assuming that inheritance automatically shares instance-level resources, when in fact each instance gets its own copy of attributes defined in `__init__`.

How to eliminate wrong answers

Option A is wrong because using `@classmethod` for the `log` method does not cause different file handles; it simply means the method receives the class as the first argument, not the instance. The file handle sharing issue is about where the handle is opened, not the method decorator. Option C is wrong because storing the file handle as a private attribute `__file` (name mangling) does not inherently cause different handles; it only affects attribute access from subclasses.

The core issue remains that the handle is opened per instance in `__init__`. Option D is wrong because overriding the `log` method in the subclass would change the behavior of logging, but it would not cause the file handle to be different unless the override itself opens a new handle. The question states the developer expects both classes to share the same handle, and the problem is that after creating an instance of `AppLogger`, the handle is different—this points to the handle being created per instance, not to an override.

41
MCQmedium

An engineer is debugging an application that uses inheritance. The base class 'Vehicle' defines a method 'start()' that prints 'Vehicle started'. The subclass 'Car' overrides 'start()' to print 'Car started'. The code contains a function that accepts a Vehicle object and calls 'start()'. What is the output if a Car object is passed?

A.'Car started Vehicle started'
B.'Vehicle started'
C.'Car started'
D.AttributeError
AnswerC

Polymorphism: the overridden method in Car is called.

Why this answer

When a Car object is passed to a function expecting a Vehicle reference, Python uses dynamic dispatch (late binding) to call the overridden `start()` method defined in the Car class. Since the actual runtime type is Car, the overridden version executes, printing 'Car started'. This is a fundamental principle of polymorphism in Python.

Exam trap

Python Institute often tests the misconception that the declared parameter type (Vehicle) determines which method runs, leading candidates to pick 'Vehicle started', when in fact Python always uses the actual object's type at runtime.

How to eliminate wrong answers

Option A is wrong because it suggests both the base and subclass methods execute, which would require explicit super() calls or chained execution not present in the code. Option B is wrong because it assumes static binding based on the parameter type, ignoring Python's runtime method resolution. Option D is wrong because no AttributeError occurs; Car inherits from Vehicle and correctly overrides start(), so the method exists and is callable.

42
MCQmedium

What is the output of the code?

A.20
B.AttributeError: 'Derived' object has no attribute 'get_x'
C.10
D.AttributeError: 'Derived' object has no attribute '_x'
AnswerA

Derived.__init__ sets _x to 20, and get_x returns it.

Why this answer

Option A is correct because the `Derived` class inherits the `get_x` method from the `Base` class, which returns `self._x`. When `obj.get_x()` is called, `self` refers to the `Derived` instance, and `self._x` accesses the `_x` attribute set in `Derived.__init__` (value 20). The `_x` attribute in `Derived` shadows the one in `Base`, so the output is 20.

Exam trap

Python Institute often tests the distinction between attribute shadowing and method inheritance, specifically that a derived class can override an attribute without calling the base class constructor, leading to unexpected values when inherited methods access that attribute.

How to eliminate wrong answers

Option B is wrong because `Derived` inherits `get_x` from `Base`, so the object does have that method; no AttributeError occurs. Option C is wrong because the `Derived` constructor sets `self._x = 20`, overriding the `_x = 10` set in `Base.__init__` (since `Derived.__init__` is called and does not call `super().__init__()`), so the value returned is 20, not 10. Option D is wrong because `_x` is a regular attribute (not a private name-mangled attribute), so it is accessible directly; no AttributeError occurs for `_x`.

43
MCQmedium

A programmer uses a class method to create an alternative constructor for a `Point` class. The method should parse a string like "10,20" and return a `Point` instance with x=10, y=20. Which code snippet correctly implements this?

A.`def from_string(self, s):\n parts = s.split(',')\n return Point(int(parts[0]), int(parts[1]))`
B.`@staticmethod\ndef from_string(s):\n parts = s.split(',')\n return Point(int(parts[0]), int(parts[1]))`
C.`def from_string(cls, s):\n parts = s.split(',')\n return cls(int(parts[0]), int(parts[1]))`
D.`@classmethod\ndef from_string(cls, s):\n parts = s.split(',')\n return cls(int(parts[0]), int(parts[1]))`
AnswerD

This correctly uses `@classmethod` and calls `cls` to create a new instance.

Why this answer

A class method receives the class as the first argument (`cls`) and can be used to create instances via `cls(...)`. The `@classmethod` decorator is required.

44
MCQeasy

A developer is building a simulation of different types of vehicles. They have a base class Vehicle with an attribute speed initialized in __init__. They also have a subclass Car that inherits from Vehicle and adds an attribute fuel_type. The developer wants to ensure that every time a Car object is created, it also initializes the speed attribute from the Vehicle class. Which approach should the developer use?

A.Override the __new__ method of Vehicle.
B.In Car.__init__, call Vehicle.__init__(self) manually.
C.Use the @staticmethod decorator for initialization.
D.In Car.__init__, define speed directly without calling parent's __init__.
AnswerB

This ensures the parent's __init__ executes, initializing speed.

Why this answer

Option B is correct because in Python, when a subclass overrides __init__, the parent class's __init__ is not automatically called. To ensure the speed attribute from Vehicle is initialized, the developer must explicitly call Vehicle.__init__(self) inside Car.__init__. This is a fundamental requirement of Python's inheritance mechanism for proper initialization of inherited attributes.

Exam trap

Python Institute often tests the misconception that subclass __init__ automatically calls the parent __init__, leading candidates to incorrectly assume no explicit call is needed or to choose a wrong option like D.

How to eliminate wrong answers

Option A is wrong because overriding __new__ is used for controlling object creation (e.g., singletons) and is not the standard way to initialize inherited instance attributes; __init__ is the correct place for initialization. Option C is wrong because the @staticmethod decorator defines a method that does not receive self or cls, and cannot be used to initialize instance attributes like speed or fuel_type; it is unrelated to inheritance initialization. Option D is wrong because defining speed directly in Car.__init__ without calling the parent's __init__ duplicates logic and breaks the principle of code reuse; it also risks missing any additional initialization that Vehicle.__init__ might perform in the future.

45
MCQmedium

A developer is designing a system where a `Car` class needs to reuse functionality from `Engine` and `Transmission` without creating a deep hierarchy. Which OOP principle should be applied?

A.Aggregation, where Car is part of Engine.
B.Singleton pattern for Engine.
C.Multiple inheritance to inherit from both.
D.Composition over inheritance.
AnswerD

Car has an Engine and a Transmission, favoring composition.

Why this answer

Option D, 'Composition over inheritance,' is correct because it advocates building the Car class by composing it with Engine and Transmission objects (has-a relationships) rather than inheriting from them. This avoids a deep class hierarchy and provides flexibility to change or swap components at runtime, which is a key design principle in Python and OOP.

Exam trap

Python Institute often tests the distinction between 'is-a' (inheritance) and 'has-a' (composition) relationships, and the trap here is that candidates mistakenly choose multiple inheritance (Option C) because they think reusing functionality requires inheritance, ignoring the complexity and the explicit instruction to avoid a deep hierarchy.

How to eliminate wrong answers

Option A is wrong because Aggregation defines a 'has-a' relationship where the part (Engine) can exist independently of the whole (Car), but the statement 'Car is part of Engine' reverses the relationship and is semantically incorrect. Option B is wrong because the Singleton pattern ensures only one instance of a class, which is irrelevant to reusing functionality from Engine and Transmission; it solves a different problem (global state control). Option C is wrong because multiple inheritance can lead to the diamond problem and increased complexity, and the question explicitly wants to avoid a deep hierarchy; composition is the recommended alternative.

46
MCQeasy

A developer creates a Python class with a method that is intended to be overridden in subclasses. Which approach best ensures that the method is not accidentally called on the base class?

A.Use 'pass' as the method body
B.Delete the method from the base class using 'del'
C.Add a comment '# override in subclass' inside the method body
D.Raise NotImplementedError inside the method body
AnswerD

Raising NotImplementedError clearly signals the method must be overridden.

Why this answer

Raising NotImplementedError inside the base class method is the standard Python idiom for defining an abstract-like method that must be overridden in subclasses. If a subclass fails to override the method and it is called, Python will raise an explicit error at runtime, preventing accidental use of the base implementation. This approach enforces the contract that the method is intended only for subclasses, without requiring the `abc` module.

Exam trap

Python Institute often tests the distinction between documentation-based approaches (comments) and runtime enforcement (exceptions), leading candidates to mistakenly choose a comment or 'pass' as sufficient for preventing accidental base class usage.

How to eliminate wrong answers

Option A is wrong because using 'pass' as the method body creates a no-op method that silently does nothing when called on the base class, which defeats the purpose of preventing accidental invocation. Option B is wrong because deleting the method from the base class with 'del' would cause an AttributeError when the method is called on a base class instance, but it also prevents subclasses from inheriting and overriding the method, breaking the intended design. Option C is wrong because adding a comment '# override in subclass' inside the method body has no runtime effect; it is merely a documentation hint that does not enforce or prevent any behavior.

47
MCQhard

A developer wants a class 'LoggedDict' that behaves like a dict but logs all attribute access in the console. Which method override correctly implements this for getting an attribute?

A.def __get__(self, instance, owner): print(f'Access'); return self
B.def __getattribute__(self, name): print(f'Access {name}'); return super().__getattribute__(name)
C.def __getattr__(self, name): print(f'Access {name}'); return self.__dict__[name]
D.def __getitem__(self, key): print(f'Access {key}'); return dict.__getitem__(self, key)
AnswerB

This intercepts all attribute accesses, logs them, and then delegates to the normal lookup.

Why this answer

Option B is correct because `__getattribute__` is the universal method called for every attribute access on an object. By overriding it, the developer can log the attribute name before delegating to the superclass implementation via `super().__getattribute__(name)`, which preserves the normal attribute lookup chain. This ensures that all attribute accesses (including those that exist and those that don't) are logged, which is the requirement for 'LoggedDict'.

Exam trap

Python Institute often tests the distinction between `__getattribute__` (called for every attribute access) and `__getattr__` (called only as a fallback when the attribute is not found), leading candidates to mistakenly choose `__getattr__` because it seems simpler or because they confuse it with the general 'get attribute' concept.

How to eliminate wrong answers

Option A is wrong because `__get__` is the descriptor protocol method, invoked when an attribute is accessed on a class that owns a descriptor instance, not for general attribute access on a dict-like object. Option C is wrong because `__getattr__` is only called when normal attribute lookup fails (i.e., when `__getattribute__` raises an AttributeError), so it would not log successful accesses; additionally, using `self.__dict__[name]` bypasses the dict's own storage and can cause infinite recursion or missing keys. Option D is wrong because `__getitem__` is used for subscription access (e.g., `obj[key]`), not for attribute access (e.g., `obj.attr`); it would log dictionary key lookups, not attribute accesses.

48
MCQhard

You are working on a legacy system that processes financial transactions. The system uses a class hierarchy: Transaction (base), Deposit, Withdrawal, Transfer. Each subclass overrides a method 'process()' to handle its specific logic. The code often runs in a multi-threaded environment and you notice intermittent errors where a transaction is processed twice. The logging shows that the same transaction object is being passed to the process method multiple times. The transaction objects are created from a factory function that caches recently used transactions. The errors seem to occur when two threads call the factory at the same time with the same parameters. After investigating, you find that the factory uses a class-level dictionary to cache objects. Which of the following is the most appropriate solution to prevent double processing?

A.Add a lock around the cache lookup and creation in the factory function
B.Add a flag to each transaction object to indicate if it has been processed, and check it at the start of process()
C.Remove the caching mechanism from the factory function to ensure new objects are always created
D.Make the process() method idempotent by checking if the transaction has already been applied to the account (e.g., check balance changes)
AnswerD

Idempotency ensures that repeated calls do not cause duplicate effects, which is the safest approach in a multi-threaded environment.

Why this answer

Option D is correct because the core issue is that the same transaction object can be processed multiple times in a multi-threaded environment, even if the factory is fixed. Making process() idempotent by checking whether the transaction has already been applied (e.g., verifying account balance changes) ensures that repeated calls with the same object do not cause duplicate financial effects, directly addressing the symptom of double processing regardless of how the object is cached or retrieved.

Exam trap

Python Institute often tests the misconception that preventing object reuse or adding locks in the factory is sufficient to fix double processing, when the real requirement is to make the operation itself idempotent to handle any scenario where the same object is processed more than once.

How to eliminate wrong answers

Option A is wrong because adding a lock around the cache lookup and creation only prevents race conditions in the factory, but does not prevent the same transaction object from being passed to process() multiple times after it has been created; the double processing can still occur if the object is reused or if the calling code erroneously invokes process() again. Option B is wrong because adding a processed flag to the transaction object is not thread-safe without additional synchronization; two threads could both check the flag before either sets it, leading to a race condition where both proceed to process the transaction, and it also violates the principle of keeping processing logic separate from state management. Option C is wrong because removing the caching mechanism eliminates the performance benefit of reusing objects but does not solve the fundamental problem: the same transaction object could still be passed to process() multiple times from other parts of the code, and without idempotency, double processing would still occur.

49
Multi-Selecteasy

Which TWO of the following statements about class attributes in Python are true?

Select 2 answers
A.Class attributes are always immutable.
B.Class attributes are shared by all instances.
C.Class attributes are defined inside methods.
D.Modifying a class attribute via an instance modifies it for all instances.
E.Class attributes can be accessed via the class name.
AnswersB, E

True – all instances see the same class attribute unless shadowed by an instance attribute.

Why this answer

Class attributes are accessible via the class name and are shared across all instances. Options C, D, and E are false because class attributes can be mutable (e.g., lists), modifying via an instance creates an instance attribute if assigned, and they are defined directly in the class body, not inside methods.

50
MCQmedium

A company is developing a scientific simulation framework where many different solvers must be interchangeable. The framework should enforce that each solver implements methods 'initialize' and 'step'. Developers want to use abstract base classes. Which approach should the team take to ensure that any subclass of 'Solver' cannot be instantiated unless it defines both methods?

A.Define an interface in a separate module and check using isinstance
B.Use @abstractmethod without inheriting from ABC
C.Inherit from ABC and decorate both methods with @abstractmethod
D.Define Solver with methods that raise NotImplementedError
AnswerC

This enforces that subclasses must provide implementations; they cannot be instantiated otherwise.

Why this answer

Option C is correct because inheriting from `ABC` (from the `abc` module) and decorating both `initialize` and `step` with `@abstractmethod` enforces that any concrete subclass must override these methods. Attempting to instantiate a subclass that does not implement all abstract methods raises a `TypeError`, ensuring compile-time-like safety at runtime.

Exam trap

Python Institute often tests the misconception that `@abstractmethod` alone (without inheriting from `ABC`) is sufficient to prevent instantiation, or that raising `NotImplementedError` is equivalent to abstract base class enforcement.

How to eliminate wrong answers

Option A is wrong because using `isinstance` checks against an interface in a separate module does not enforce method implementation at instantiation time; it only checks type membership, and the developer would have to manually verify methods. Option B is wrong because `@abstractmethod` without inheriting from `ABC` has no effect — Python's abstract mechanism only works when the class's metaclass is `ABCMeta` (provided by inheriting from `ABC`). Option D is wrong because defining methods that raise `NotImplementedError` only catches missing implementations at runtime when the method is called, not at instantiation time, and does not prevent instantiation of the class itself.

51
MCQhard

You are designing a class that should behave like a sequence and support slicing. Which special methods must be implemented?

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

These two methods are the minimum for a sequence that supports slicing.

Why this answer

For a class to support slicing in Python, it must implement both `__getitem__` (to handle indexing and slice objects) and `__len__` (to define the sequence length, which is required for proper slice boundary handling). Together, these satisfy the sequence protocol, enabling Python's slicing syntax like `obj[start:stop:step]`.

Exam trap

Python Institute often tests the misconception that `__getitem__` alone is enough for slicing, but the trap is that `__len__` is also required for the interpreter to handle slice defaults and negative indices correctly.

How to eliminate wrong answers

Option A is wrong because `__len__` and `__contains__` are not sufficient for slicing; `__contains__` only supports the `in` operator, not indexing or slicing. Option B is wrong because `__iter__` and `__next__` make an object iterable but do not provide indexed access or slicing capabilities. Option C is wrong because `__getitem__` alone can handle basic indexing, but without `__len__`, Python cannot properly compute slice defaults (e.g., `None` for start/stop) or support negative indices in slicing.

Option E is wrong because `__setitem__` is for item assignment, not required for read-only slicing; the sequence protocol for slicing only mandates `__getitem__` and `__len__`.

52
MCQhard

A development team is building a real-time chat application using Python. The application uses a class 'ChatRoom' that maintains a list of 'User' objects as active participants. Each User object holds a reference back to its ChatRoom to send messages. Over time, the application runs out of memory. Profiling reveals that User objects are not being garbage collected even after users disconnect. The team suspects circular references. Which solution would effectively resolve the memory leak without breaking the functionality?

A.Use weakref.WeakSet for the participants list in ChatRoom, so that when a User is no longer referenced elsewhere, it is automatically removed
B.Increase the Python heap size using PYTHON_MALLOC_DEBUG to avoid memory issues
C.Store the ChatRoom reference in User using a weakref.ref, so that the cycle is broken
D.Manually call gc.collect() every time a user disconnects
AnswerA

WeakSet allows the chat room to hold references without preventing garbage collection; when the only strong references to a User are gone, it is cleaned up.

Why this answer

Option A is correct because using a `weakref.WeakSet` for the participants list in `ChatRoom` means the `ChatRoom` holds only weak references to `User` objects. When a user disconnects and all external references to that `User` are removed, the `User` object becomes unreachable and can be garbage collected, even though the `User` still holds a strong reference back to the `ChatRoom`. This breaks the circular reference without requiring manual intervention or altering the `User`-to-`ChatRoom` relationship.

Exam trap

Python Institute often tests the distinction between breaking a cycle from one side versus the other; the trap here is that candidates think weakening the `User`'s reference to `ChatRoom` (Option C) is sufficient, but they overlook that the `ChatRoom`'s strong reference to `User` keeps the `User` alive, so the cycle is still unbreakable from the garbage collector's perspective.

How to eliminate wrong answers

Option B is wrong because increasing the Python heap size does not resolve the underlying issue of circular references preventing garbage collection; it only delays the inevitable memory exhaustion. Option C is wrong because storing the `ChatRoom` reference in `User` using `weakref.ref` would break the cycle from the `User` side, but the `ChatRoom` still holds strong references to `User` objects in its participants list, so `User` objects would never become unreachable and would still leak. Option D is wrong because manually calling `gc.collect()` does not fix the root cause; the garbage collector can already collect cycles (by default), but if the `User` objects are still strongly referenced from the `ChatRoom` list, they are not garbage, and `gc.collect()` will not remove them.

53
MCQeasy

A class `Point` has an `__init__` that sets `self.x` and `self.y`. They want to compare points by equality (i.e., `p1 == p2` should work correctly). Which method should they implement?

A.`__same__`
B.`__eq__`
C.`__compare__`
D.`__cmp__`
AnswerB

`__eq__` is the correct method to override for the equality operator.

Why this answer

In Python, the `__eq__` method is the correct way to define equality comparison for objects. When you implement `__eq__(self, other)`, it is automatically called by the `==` operator, allowing `p1 == p2` to return a Boolean based on custom logic (e.g., comparing `self.x` and `self.y`). This is the standard Python protocol for equality testing.

Exam trap

Python Institute often tests the distinction between Python 2's `__cmp__` and Python 3's `__eq__`; the trap here is that candidates familiar with older Python may incorrectly choose `__cmp__`, not realizing it is obsolete in Python 3.

How to eliminate wrong answers

Option A is wrong because `__same__` is not a Python special method; Python uses `__eq__` for equality, not `__same__`. Option C is wrong because `__compare__` is not a Python dunder method; Python uses `__eq__` for equality and `__lt__`, `__gt__`, etc. for ordering. Option D is wrong because `__cmp__` was used in Python 2 for comparison (returning -1, 0, 1) but was removed in Python 3; the PCAP exam focuses on Python 3, where `__eq__` is the correct method for equality.

54
MCQhard

Which of the following correctly uses an abstract base class to enforce that all subclasses implement a 'make_sound' method? (Assume ABC imported)

A.from abc import ABC, abstractmethod\nclass Animal(ABC):\n @abstractmethod\n def make_sound(self):\n pass
B.from abc import abstractmethod\nclass Animal:\n @abstractmethod\n def make_sound(self):\n pass
C.class Animal:\n def make_sound(self):\n return None
D.class Animal:\n def make_sound(self):\n raise NotImplementedError
AnswerA

Proper ABC with abstractmethod.

Why this answer

Option A is correct because it imports both `ABC` and `abstractmethod` from the `abc` module, defines `Animal` as a subclass of `ABC`, and decorates `make_sound` with `@abstractmethod`. This combination prevents instantiation of `Animal` and forces any concrete subclass to override `make_sound`, or else a `TypeError` is raised at instantiation time.

Exam trap

Python Institute often tests whether candidates know that `@abstractmethod` alone does not make a class abstract — the class must explicitly inherit from `ABC` (or have its metaclass set to `ABCMeta`), otherwise the decorator is ignored and instantiation is allowed.

How to eliminate wrong answers

Option B is wrong because it does not make `Animal` a subclass of `ABC`; without inheriting from `ABC`, the `@abstractmethod` decorator has no effect and the class can be instantiated directly, so no enforcement occurs. Option C is wrong because it defines a concrete method that simply returns `None`; subclasses are free to ignore it, and there is no abstract mechanism to require overriding. Option D is wrong because raising `NotImplementedError` is a runtime convention, not a compile-time or instantiation-time enforcement; a subclass that forgets to override `make_sound` will only fail when the method is called, not when the object is created, and the base class is not abstract.

55
Multi-Selecthard

Which THREE methods must be implemented to create an immutable object that supports iteration and membership testing?

Select 3 answers
A.__init__
B.__getitem__
C.__len__
D.__contains__
E.__iter__
AnswersB, C, E

Supports indexing and slicing, and can be used for iteration.

Why this answer

Option B is correct because implementing __getitem__ allows indexing and slicing, which is essential for iteration in Python when __iter__ is not defined; Python falls back to __getitem__ for iteration. This method also supports membership testing via the 'in' operator when __contains__ is absent, as Python will iterate through the object to check membership.

Exam trap

Python Institute often tests the fallback behavior of Python's iteration and membership protocols, leading candidates to incorrectly assume __contains__ is mandatory for membership testing when it is actually optional if __iter__ or __getitem__ is implemented.

56
Multi-Selecteasy

Which TWO of the following are special methods in Python?

Select 2 answers
A.`__bar__`
B.`__main__`
C.`__init__`
D.`__str__`
E.`__foo__`
AnswersC, D

This is the instance initializer method.

Why this answer

Option C is correct because `__init__` is a predefined special method in Python used as a constructor to initialize an object's state when an instance of a class is created. It is automatically invoked by the Python runtime upon object instantiation, making it a core part of the object-oriented programming model in Python.

Exam trap

Python Institute often tests the distinction between actual special methods (like `__init__` and `__str__`) and arbitrary dunder-named attributes that are not part of Python's language specification, leading candidates to mistakenly think any name with double underscores is a special method.

57
Multi-Selecteasy

Which TWO of the following statements about Python class inheritance are correct? (Select exactly two.)

Select 2 answers
A.If a subclass does not define __init__, it automatically inherits the parent's __init__.
B.A subclass can override a parent class method.
C.A subclass cannot define methods with the same name as a parent class method.
D.A subclass inherits all private attributes and methods from the parent class.
E.A class can inherit from multiple base classes.
AnswersB, E

Method overriding is a key OOP feature.

Why this answer

Option B is correct because Python allows a subclass to define a method with the same name as a method in its parent class, which overrides the parent's implementation. When the method is called on an instance of the subclass, Python's method resolution order (MRO) will find the subclass's version first, effectively hiding the parent's method.

Exam trap

Python Institute often tests the misconception that private attributes (name-mangled with double underscore) are completely inaccessible from subclasses, but in reality they are only renamed and can still be accessed using the mangled name, so they are not truly private.

58
MCQmedium

Refer to the exhibit. What is the output when the code is executed?

A.Default x
B.HELLO
C.hello
D.AttributeError
AnswerB

Correct: __setattr__ converts to uppercase.

Why this answer

Option B is correct because the code defines a class `X` with a class attribute `x = 'Default x'` and a `__str__` method that returns `'HELLO'`. When `print(obj)` is called, Python invokes the `__str__` method of the object, which returns `'HELLO'`, so the output is `HELLO`. The class attribute `x` is never accessed in the `__str__` method, so it is ignored.

Exam trap

Python Institute often tests the distinction between class attributes and the `__str__` method, trapping candidates who assume that a class attribute named `x` will be printed automatically, rather than recognizing that `__str__` defines the output explicitly.

How to eliminate wrong answers

Option A is wrong because the `__str__` method overrides the default string representation; the class attribute `x` is not printed unless explicitly referenced. Option C is wrong because the `__str__` method returns the uppercase string `'HELLO'`, not the lowercase `'hello'`. Option D is wrong because no `AttributeError` occurs; the `__str__` method is defined correctly and returns a string, so the print statement executes without error.

59
MCQhard

A Python program uses multiple inheritance. Class A defines method 'foo', class B also defines 'foo', and class C(A, B) inherits from both. The developer expects that calling 'foo' on an instance of C should invoke the method from class A. However, it invokes the method from class B. What is the most likely cause?

A.The method resolution order (MRO) is not as expected due to the order of base classes
B.Python always calls the method from the last base class
C.The 'foo' method in class B is defined after class A's 'foo' in the same file
D.Python uses depth-first, left-to-right resolution
AnswerA

MRO uses C3 linearization; if B's method is called, the MRO likely lists B before A, possibly due to a different inheritance structure.

Why this answer

Option A is correct because Python's Method Resolution Order (MRO) uses the C3 linearization algorithm, which respects the order of base classes as listed in the class definition. When class C(A, B) is defined, the MRO is C → A → B, so 'foo' from A should be found first. However, if the developer mistakenly wrote C(B, A), the MRO becomes C → B → A, causing B's 'foo' to be invoked.

The question states the developer expected A's method but got B's, indicating the base class order was reversed.

Exam trap

Python Institute often tests the misconception that Python uses simple depth-first, left-to-right resolution (like some older languages), when in fact it uses the C3 linearization algorithm, and the order of base classes in the class definition is the primary factor determining which method is called.

How to eliminate wrong answers

Option B is wrong because Python does not always call the method from the last base class; it follows the MRO computed by C3 linearization, which depends on the order of base classes and the inheritance hierarchy. Option C is wrong because the order in which methods are defined in the source file has no effect on MRO; Python resolves methods based on the class hierarchy and the C3 algorithm, not lexical order. Option D is wrong because Python does not use simple depth-first, left-to-right resolution; it uses the C3 linearization algorithm, which ensures monotonicity and handles diamond inheritance correctly, avoiding the issues of naive DFS.

60
MCQmedium

Given: class A: def method(self): print('A'); class B(A): def method(self): super().method(); print('B'); class C(A): def method(self): super().method(); print('C'); class D(B, C): pass. What is printed by D().method()?

A.A B C
B.A C B
C.C A B
D.B A C
AnswerB

Correct call order via MRO.

Why this answer

Option B is correct because Python's MRO (Method Resolution Order) for class D, which inherits from B and C (both inheriting from A), follows the C3 linearization algorithm. The MRO for D is D -> B -> C -> A, so calling D().method() triggers B.method(), which calls super().method() (resolving to C.method()), which calls super().method() (resolving to A.method()), printing 'A', then back to C prints 'C', then back to B prints 'B', resulting in 'A C B'.

Exam trap

Python Institute often tests the misconception that super() always calls the immediate parent class (A) in a linear chain, rather than following the full MRO, leading candidates to pick 'A B C' instead of the correct 'A C B'.

How to eliminate wrong answers

Option A is wrong because it assumes a simple left-to-right depth-first order without considering that super() in B resolves to C (the next class in MRO), not directly to A, so the output is not 'A B C'. Option C is wrong because it incorrectly suggests C.method() is called first, but the MRO starts with D, then B, not C. Option D is wrong because it implies B.method() prints 'B' before its super() chain completes, but the actual order is A (from A.method), then C (from C.method), then B (from B.method).

61
MCQhard

A developer is implementing a caching system where object instances should be reused if they have the same set of initialization parameters. They want to control object creation so that calling MyClass(param1, param2) returns the same instance if it has been created before, otherwise creates a new one. They are considering overriding __new__ to implement this caching. However, they also need to ensure that __init__ is not called again for cached instances. What is the correct way to achieve this?

A.Override __new__ to return a cached instance, and override __init__ to do nothing if already initialized.
B.Use a class method as an alternative constructor and store instances in a class-level dictionary.
C.Override __call__ on the class metaclass.
D.Override __new__ to return a cached instance if exists, and skip __init__ by checking if the instance is from cache.
AnswerB

This avoids the __new__/__init__ issue entirely by not using the constructor directly; the class method controls caching.

Why this answer

Option B is correct because using a class method as an alternative constructor allows the developer to control instance creation and reuse without interfering with Python's normal object creation protocol. By storing instances in a class-level dictionary keyed by the initialization parameters, the class method can return a cached instance if it exists, or create a new one via the normal constructor (which calls __new__ and __init__). This approach cleanly avoids calling __init__ on cached instances, since the cached object is simply returned directly without invoking the constructor again.

Exam trap

Python Institute often tests the misconception that overriding __new__ alone can prevent __init__ from being called on cached instances, but in reality Python always calls __init__ on the object returned by __new__ unless the returned object is of a different type.

How to eliminate wrong answers

Option A is wrong because overriding __init__ to do nothing if already initialized does not prevent __init__ from being called; Python always calls __init__ on the object returned by __new__, even if that object already exists. Option C is wrong because overriding __call__ on the metaclass changes how the class itself is called (i.e., when you call the class to create an instance), but it does not directly solve the problem of skipping __init__ for cached instances; it would require additional logic to bypass __init__, which is not straightforward. Option D is wrong because even if __new__ returns a cached instance, Python will still call __init__ on that instance unless the object's class is a different type or __init__ is explicitly bypassed via a custom mechanism (e.g., using a flag), which is error-prone and not a standard pattern.

62
Multi-Selecteasy

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

Select 2 answers
A.Define a method named `property` inside the class.
B.Override `__getattribute__` and check the attribute name.
C.Override `__getattr__` to simulate a property.
D.Use `property(getter, setter)` as a class variable.
E.Use the @property decorator on a method.
AnswersD, E

Correct: property() creates a property descriptor.

Why this answer

Option D is correct because `property(getter, setter)` is a built-in function that returns a property object, which can be assigned as a class variable to define a managed attribute. Option E is correct because the `@property` decorator is the standard, concise way to define a read-only property by decorating a method that acts as the getter.

Exam trap

Python Institute often tests the distinction between defining a property via the `property()` function or `@property` decorator versus overriding dunder methods like `__getattr__` or `__getattribute__`, which are attribute interception hooks, not property definitions.

63
Multi-Selecthard

Which THREE statements about the Python method resolution order (MRO) are true? (Select exactly 3.)

Select 3 answers
A.The MRO is determined at runtime when a method is called.
B.The MRO of a class can be viewed using the __mro__ attribute.
C.C3 linearization is the algorithm used for MRO in Python 3.
D.Python uses a depth-first left-to-right algorithm for MRO.
E.super() uses the MRO to determine which method to call.
AnswersB, C, E

Correct: __mro__ is a tuple of classes in MRO order.

Why this answer

Option B is correct because the `__mro__` attribute on a class returns a tuple of classes in the exact order that Python uses to resolve methods and attributes. This attribute is computed at class definition time using the C3 linearization algorithm, and it provides a direct, read-only view of the resolution order for that class.

Exam trap

Python Institute often tests the misconception that MRO is determined dynamically at runtime (option A) or that Python still uses a simple depth-first left-to-right algorithm (option D), when in fact Python 3 exclusively uses the C3 linearization algorithm computed at class definition time.

64
Multi-Selecteasy

Which TWO statements about static methods (@staticmethod) are correct?

Select 2 answers
A.They do not receive an implicit first argument.
B.They can access instance attributes via self.
C.They can access class variables only via cls.
D.They are defined using the @staticmethod decorator.
E.They can only be defined inside a metaclass.
AnswersA, D

Correct: no self or cls.

Why this answer

Option A is correct because static methods in Python do not receive an implicit first argument like `self` (for instance methods) or `cls` (for class methods). They behave like plain functions but belong to a class's namespace, and are called without any automatic parameter injection.

Exam trap

Python Institute often tests the distinction between `@staticmethod` and `@classmethod`, and the trap here is that candidates confuse static methods with class methods, assuming static methods can access class variables via `cls` or instance attributes via `self`.

65
Multi-Selecthard

Which THREE of the following are true about Python's object-oriented programming features?

Select 3 answers
A.Python supports method overloading based on argument types
B.Python supports multiple inheritance
C.All methods are virtual in the sense that they can be overridden
D.Python enforces access modifiers like private and protected
E.Operator overloading can be implemented by defining special methods like __add__
AnswersB, C, E

Yes, a class can inherit from multiple base classes.

Why this answer

Option B is correct because Python's class hierarchy supports multiple inheritance, allowing a class to inherit from more than one parent class. This is a core feature of Python's object-oriented programming model, implemented via the C3 linearization algorithm (Method Resolution Order, or MRO) to resolve method and attribute lookups unambiguously.

Exam trap

Python Institute often tests the misconception that Python supports method overloading like Java or C++, leading candidates to incorrectly select Option A, when in fact Python uses dynamic typing and late binding to handle different argument patterns through default or variable arguments.

66
Multi-Selecteasy

Which of the following statements about class inheritance in Python are true? (Choose two.)

Select 2 answers
A.A class can inherit from only one base class.
B.Abstract base classes can be defined using the ABC module.
C.A child class can override any method from its parent class, but only if the method is declared as virtual.
D.The super() function is used to call a method from a sibling class.
E.Python supports multiple inheritance.
AnswersB, E

The abc module provides the ABC class and abstractmethod decorator.

Why this answer

Option B is correct because Python's `abc` module (Abstract Base Classes) allows you to define abstract base classes by inheriting from `ABC` and using the `@abstractmethod` decorator. This enforces that subclasses must implement the abstract methods, providing a formal interface contract.

Exam trap

Python Institute often tests the misconception that `super()` only calls a parent method, but in multiple inheritance it actually calls the next class in the MRO, which could be a sibling or a cousin, not necessarily a direct parent.

67
MCQeasy

A developer is implementing a simple counter class. The class should start at 0 and increment by 1 each time the 'increment' method is called. Which implementation is correct?

A.class Counter:\n def __init__(self):\n self.count = 0\n def increment(self):\n count = self.count + 1
B.class Counter:\n def __init__(self):\n self.count = 0\n def increment(self):\n self.count += 1
C.class Counter:\n def __init__(self):\n count = 0\n def increment(self):\n count += 1
D.class Counter:\n def __init__(self):\n self.count = 0\n def increment():\n self.count += 1
AnswerB

Correctly defines instance variable and increments.

Why this answer

Option B is correct because it properly initializes an instance variable `self.count` to 0 in the `__init__` method and then uses `self.count += 1` in the `increment` method to modify the instance variable in place. The `+=` operator is a shorthand for `self.count = self.count + 1`, which correctly updates the counter each time `increment` is called.

Exam trap

Python Institute often tests the distinction between local variables and instance variables in methods, trapping candidates who forget to prefix `self` when accessing or modifying object attributes.

How to eliminate wrong answers

Option A is wrong because `count = self.count + 1` creates a local variable `count` inside the `increment` method instead of updating the instance variable `self.count`, so the counter never changes. Option C is wrong because `count = 0` in `__init__` creates a local variable instead of an instance variable, and `count += 1` in `increment` tries to modify a local variable that hasn't been initialized in that scope, causing a `NameError`. Option D is wrong because the `increment` method is missing the required `self` parameter, so calling `increment()` will raise a `TypeError` about missing positional arguments.

68
MCQeasy

A programmer wants to ensure that a class attribute is the same for all instances and can be accessed via the class name. Which type of variable should be defined?

A.Global variable
B.Instance variable
C.Local variable inside a method
D.Class variable
AnswerD

Class variables are shared and accessed via class name.

Why this answer

Option D is correct because a class variable in Python is defined directly within the class body (outside any method) and is shared across all instances. It can be accessed via the class name (e.g., `ClassName.var`) or through any instance, ensuring the same value for all objects.

Exam trap

Python Institute often tests the misconception that a class variable can be safely modified via an instance, but the trap is that doing so creates an instance variable that shadows the class variable, leaving the original class variable unchanged for other instances.

How to eliminate wrong answers

Option A is wrong because a global variable is defined at the module level, not inside a class, and is not inherently tied to the class or its instances; it can be modified from anywhere, breaking encapsulation. Option B is wrong because an instance variable is unique to each object (defined with `self` in `__init__`), so it is not the same for all instances. Option C is wrong because a local variable inside a method exists only within that method's scope and cannot be accessed via the class name or by other methods.

69
MCQhard

A team is developing a banking system in Python. They have a base class Account with attributes account_number and balance, and a method __init__(self, account_number, balance) that initializes these attributes. They then create a subclass SavingsAccount that adds an attribute interest_rate. In SavingsAccount's __init__, they assign self.interest_rate = rate but do not call super().__init__. When they instantiate SavingsAccount('12345', 1000, 0.02) and attempt to print(balance), an AttributeError occurs: 'SavingsAccount' object has no attribute 'balance'. What is the most appropriate fix to ensure that the SavingsAccount includes balance and account_number without code duplication?

A.Remove the __init__ method from SavingsAccount entirely and rely on the default __init__ from Account.
B.Manually assign self.account_number and self.balance in SavingsAccount.__init__ before assigning interest_rate.
C.Call super().__init__(account_number, balance) as the first line in SavingsAccount.__init__, then assign self.interest_rate = rate.
D.Define balance as a class attribute in Account with a default value and remove it from __init__.
AnswerC

This correctly delegates to the base class and adds the new attribute.

Why this answer

The fix is to call super().__init__ to reuse the base class initialization and then add the subclass-specific attribute. Option A duplicates code, option C changes the architecture unnecessarily, and option D removes necessary initialization.

70
MCQhard

An abstract base class (ABC) defines an abstract method `process()`. Several subclasses implement it. A function accepts any subclass and calls `process()`. This demonstrates which OOP concept?

A.Inheritance.
B.Encapsulation.
C.Method overloading.
D.Polymorphism.
AnswerD

The same interface (process) works differently for different subclasses.

Why this answer

Option D is correct because polymorphism allows objects of different subclasses to be treated uniformly through their common interface. When a function accepts any subclass of the ABC and calls `process()`, the correct implementation is resolved at runtime via dynamic dispatch, which is the essence of polymorphism in Python.

Exam trap

Python Institute often tests the distinction between inheritance and polymorphism by presenting a scenario where multiple subclasses override a method, leading candidates to mistakenly select 'inheritance' because they see the class hierarchy, but the key behavior is the polymorphic call, not the inheritance itself.

How to eliminate wrong answers

Option A is wrong because inheritance alone only establishes a parent-child relationship and code reuse; it does not guarantee that the same method call behaves differently across subclasses. Option B is wrong because encapsulation is about bundling data and methods together and restricting direct access to internal state, which is not demonstrated by calling a method on different subclass objects. Option C is wrong because method overloading refers to multiple methods with the same name but different parameters within the same class, which Python does not support natively; the scenario here uses a single method signature overridden in subclasses, not overloading.

71
MCQmedium

An application uses a class to represent a configuration object that reads settings from a file. The class has a class attribute config_cache that holds a dictionary of loaded configurations to avoid repeated file reads. However, the developer notices that when they modify the dictionary for one instance, it affects all instances. They want to ensure that each instance has its own copy of the configuration data upon initialization. Which change should they make?

A.Move the dictionary initialization into the __init__ method so each instance creates its own dictionary.
B.Use a @staticmethod to return a new dictionary each time.
C.Keep the class attribute but use a deep copy in __init__ before modifying.
D.Define the dictionary inside a class method.
AnswerA

Initializing in __init__ creates a new dictionary per instance, avoiding sharing.

Why this answer

Option A is correct because moving the dictionary initialization into the __init__ method ensures that each instance gets its own separate dictionary object. Class attributes are shared across all instances, so modifying the dictionary via one instance changes it for all. By assigning `self.config_cache = {}` inside __init__, each instance creates a new, independent dictionary upon instantiation, solving the shared-state problem.

Exam trap

Python Institute often tests the distinction between mutable and immutable class attributes, trapping candidates who think a deep copy in __init__ will fix the sharing issue, when in fact the shared reference to the class attribute itself must be replaced with an instance attribute.

How to eliminate wrong answers

Option B is wrong because a @staticmethod that returns a new dictionary would still need to be called and assigned to an instance attribute; if the result is stored in a class attribute, the sharing issue persists. Option C is wrong because using a deep copy in __init__ before modifying does not prevent the initial shared reference; the class attribute itself remains a single dictionary that all instances point to, so any modification to the original (or a copy made later) still affects the shared object. Option D is wrong because defining the dictionary inside a class method does not change its scope; if the method assigns to a class attribute, it remains shared, and if it returns a new dict, the instance must still store it properly to avoid sharing.

72
MCQeasy

A developer wants to ensure that a class attribute is shared among all instances but cannot be modified from outside the class. Which approach is most appropriate?

A.Use a property decorator on a class method
B.Define a public class attribute and document it as read-only
C.Define an instance attribute inside __init__
D.Define a private class attribute (e.g., __shared) and provide a class method to access it
AnswerD

Name mangling discourages direct access; getter method controls read-only access.

Why this answer

Option D is correct because defining a private class attribute with name mangling (e.g., `__shared`) prevents direct external modification, and providing a class method (using `@classmethod`) allows read-only access to the attribute. This ensures the attribute is shared among all instances (since it belongs to the class, not instances) while enforcing encapsulation.

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 works on instance methods) with class-level read-only access, or assume documentation alone provides protection.

How to eliminate wrong answers

Option A is wrong because a property decorator is designed for instance attributes, not class attributes; applying it to a class method would not create a shared, read-only class-level attribute. Option B is wrong because documenting a public class attribute as read-only does not enforce immutability — external code can still modify it directly, violating the requirement. Option C is wrong because an instance attribute defined inside `__init__` is unique to each instance, not shared among all instances.

73
MCQeasy

During code review, a team member wrote: 'class Dog(Animal): pass'. The Animal class has an __init__ method that requires a 'name' parameter. When instantiating Dog, what should be done to properly initialize the inherited attributes?

A.Define a new __init__ in Dog that does not call super
B.Dog will automatically inherit the __init__ method
C.Use a decorator to initialize
D.Define __init__ in Dog that calls super().__init__(name)
AnswerD

Proper way to extend parent initialization.

Why this answer

Option D is correct because in Python, when a subclass like Dog inherits from a superclass like Animal, the __init__ method is not automatically called unless explicitly invoked. Since Animal's __init__ requires a 'name' parameter, Dog must define its own __init__ and call super().__init__(name) to ensure the inherited attributes are properly initialized. This follows Python's method resolution order (MRO) and is the standard pattern for cooperative multiple inheritance.

Exam trap

Python Institute often tests the misconception that inheritance automatically runs the parent's __init__ when a subclass is instantiated, leading candidates to incorrectly select Option B without realizing that Python requires explicit super() calls for proper initialization.

How to eliminate wrong answers

Option A is wrong because defining a new __init__ in Dog that does not call super() will override the parent's __init__, leaving the inherited 'name' attribute uninitialized and potentially causing AttributeError when accessing it. Option B is wrong because while Dog does inherit the __init__ method from Animal, it is not automatically called when instantiating Dog; Python only calls __init__ if it is defined in the class or inherited, but the inherited method still requires explicit invocation via super() to run properly in the subclass context. Option C is wrong because Python does not have a built-in decorator for initializing inherited attributes; decorators like @dataclass or @property serve different purposes and do not replace the need for explicit super().__init__() calls.

74
MCQmedium

A team is implementing a shape hierarchy with a base class `Shape` that should have an `area()` method. They want to ensure that every subclass must provide its own implementation of `area()`. Which approach should they use?

A.Define `area()` in `Shape` and have it raise `NotImplementedError`.
B.Use a class method that must be overridden.
C.Define `area()` as a property that raises an error.
D.Use `@abstractmethod` from the `abc` module to declare `area()` as abstract.
AnswerD

This enforces that subclasses must implement `area()` and prevents instantiation of `Shape` directly.

Why this answer

Option D is correct because the `abc` module provides the `ABCMeta` metaclass and the `@abstractmethod` decorator, which together enforce that any concrete subclass must override the abstract method. If a subclass fails to implement `area()`, Python raises a `TypeError` at instantiation time, ensuring the design contract is upheld. This is the standard Pythonic way to define abstract base classes and enforce method implementation in subclasses.

Exam trap

Python Institute often tests the distinction between raising `NotImplementedError` (a runtime-only check) and using `@abstractmethod` (which prevents instantiation of incomplete subclasses), leading candidates to mistakenly choose Option A because they think 'raising an error' is sufficient for enforcement.

How to eliminate wrong answers

Option A is wrong because raising `NotImplementedError` at runtime does not enforce compile-time or instantiation-time checks; a subclass can forget to override `area()` and the error will only appear when the method is called, not when the object is created. Option B is wrong because a class method (`@classmethod`) is not designed for abstract method enforcement; it can be overridden but there is no built-in mechanism to require overriding, and the `@abstractmethod` decorator is the correct tool for that purpose. Option C is wrong because defining `area()` as a property that raises an error does not prevent instantiation of a subclass that fails to override the property; the error only occurs when the property is accessed, and properties are not intended for abstract method enforcement.

75
MCQmedium

Consider a class `D` that inherits from multiple base classes `B` and `C`. The developer wants to call a method from a specific parent class while ensuring correct method resolution order (MRO). Which is the safest way?

A.`self.method()`
B.`ParentClass.method(self)`
C.`super().method()`
D.`BaseClass.method(self)`
AnswerC

`super()` follows the MRO, calling the method from the next class in line.

Why this answer

In Python, `super().method()` is the safest way to call a method from a parent class in a multiple inheritance scenario because it respects the Method Resolution Order (MRO) defined by the C3 linearization algorithm. This ensures that the method is resolved from the next class in the MRO, avoiding hard-coded references that could break if the inheritance hierarchy changes. It also correctly handles cooperative multiple inheritance, where each class in the MRO can collaborate via `super()` calls.

Exam trap

Python Institute often tests the misconception that `super()` only calls the immediate parent class, when in fact it follows the full MRO, and candidates mistakenly choose a hard-coded parent call (like Option B or D) thinking it is more explicit and safer.

How to eliminate wrong answers

Option A is wrong because `self.method()` will invoke the method on the instance using the MRO, starting from the class of `self`, which may not call the intended parent class method if the method is overridden in a subclass. Option B is wrong because `ParentClass.method(self)` is a hard-coded reference that bypasses the MRO entirely, leading to potential issues in diamond inheritance or if the class hierarchy is modified. Option D is wrong because `BaseClass.method(self)` is essentially the same as Option B — it directly calls a specific base class method, ignoring the MRO and breaking cooperative multiple inheritance patterns.

Page 1 of 2 · 138 questions totalNext →

Ready to test yourself?

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