Try 10 focused PCAP-31-03 questions on OOP, with explanations, then continue with IT Mastery.
Open the matching IT Mastery practice page for timed mocks, topic drills, progress tracking, explanations, and full practice.
| Field | Detail |
|---|---|
| Exam route | PCAP-31-03 |
| Topic area | Section 4: Object-Oriented Programming |
| Blueprint weight | 34% |
| Page purpose | Focused sample questions before returning to mixed practice |
Use this page to isolate Section 4: Object-Oriented Programming for PCAP-31-03. Work through the 10 questions first, then review the explanations and return to mixed practice in IT Mastery.
| Pass | What to do | What to record |
|---|---|---|
| First attempt | Answer without checking the explanation first. | The fact, rule, calculation, or judgment point that controlled your answer. |
| Review | Read the explanation even when you were correct. | Why the best answer is stronger than the closest distractor. |
| Repair | Repeat only missed or uncertain items after a short break. | The pattern behind misses, not the answer letter. |
| Transfer | Return to mixed practice once the topic feels stable. | Whether the same skill holds up when the topic is no longer obvious. |
Blueprint context: 34% of the practice outline. A focused topic score can overstate readiness if you recognize the pattern too quickly, so use it as repair work before timed mixed sets.
These questions are original IT Mastery practice items aligned to this topic area. They are designed for self-assessment and are not official exam questions.
Topic: Section 4: Object-Oriented Programming
A developer inspects which attributes are stored directly on an instance versus on its class. What is printed by this code?
class Sample:
level = 1
__flag = "class"
def __init__(self):
self.name = "obj"
self.__flag = "inst"
s = Sample()
s.level = 3
print(sorted(s.__dict__.keys()))
print(sorted(k for k in Sample.__dict__.keys() if "flag" in k or k == "level"))
print(s.__dict__.get("level"), Sample.__dict__["level"])
print(s.__dict__["_Sample__flag"], Sample.__dict__["_Sample__flag"])
Options:
A. [’__flag’, ’level’, ’name’] [’__flag’, ’level’] 3 1 inst class
B. [’_Sample__flag’, ’name’] [’_Sample__flag’, ’level’] 1 1 inst class
C. [’_Sample__flag’, ’level’, ’name’] [’_Sample__flag’, ’level’] 3 1 class inst
D. [’_Sample__flag’, ’level’, ’name’] [’_Sample__flag’, ’level’] 3 1 inst class
Best answer: D
Explanation: An instance __dict__ contains only attributes stored on that object, while a class __dict__ contains class-level attributes. Here, s.level = 3 creates an instance attribute named level, and __flag is name-mangled to _Sample__flag in both namespaces.
The key idea is that instance and class namespaces are separate. Sample.__dict__ stores class attributes such as level and the mangled private name _Sample__flag. The instance s.__dict__ stores only names assigned directly to s, which are name, _Sample__flag, and later level after s.level = 3.
Name mangling changes __flag inside Sample to _Sample__flag, so neither dictionary uses the plain name __flag. Because s.level = 3 assigns to the instance, s.__dict__ gets its own level entry with value 3, while the class still keeps level as 1.
The common mistake is to forget either name mangling or that assigning through an instance can create a separate instance attribute.
__flag is stored as _Sample__flag, not as __flag.level fails because s.level = 3 adds level to s.__dict__ and shadows the class attribute."inst", while the class private attribute remains "class".Topic: Section 4: Object-Oriented Programming
A package contains pkg/device.py:
class Sensor:
__version = 3
def __init__(self, sid):
self.__id = sid
In app.py, the code runs:
from pkg.device import Sensor
s = Sensor("A7")
Which statement prints the private instance ID and the private class version without raising an exception?
Options:
A. print(s._Sensor__id, Sensor._Sensor__version)
B. print(s._Sensor__id, Sensor.__version)
C. print(Sensor._Sensor__id, Sensor._Sensor__version)
D. print(s.__id, Sensor.__version)
Best answer: A
Explanation: Python name-mangles double-underscore attributes by prefixing them with _ClassName. Here, __id becomes _Sensor__id on the instance, and __version becomes _Sensor__version on the class.
At PCAP depth, a name like __id inside class Sensor is not truly inaccessible; Python rewrites it to a mangled form to reduce accidental access from outside the class or from subclasses. The rewritten name uses the class name as a prefix.
self.__id becomes self._Sensor__id__version inside Sensor becomes Sensor._Sensor__versionSo the instance value must be read from s._Sensor__id, while the class value must be read from Sensor._Sensor__version. Using the original double-underscore names outside the class body does not match the stored attribute names. The closest distractor is the one that correctly mangles the instance attribute but still tries to access the class attribute with its unmangled name.
s.__id and Sensor.__version fails because unmangled double-underscore names are not the stored attribute names outside the class body.Sensor._Sensor__id fails because __id is an instance attribute, not a class attribute.s._Sensor__id and Sensor.__version gets the instance part right but leaves the class attribute unmangled.Topic: Section 4: Object-Oriented Programming
A developer wants to verify the final state after updating an attribute through one instance. Which two statements are true after this code runs? Select TWO.
class Account:
fee = 10
def __init__(self, owner):
self.owner = owner
a = Account("Ann")
b = Account("Ben")
a.fee += 5
Options:
A. Reading b.fee still returns 10.
B. b now has its own instance attribute fee.
C. Account.fee is changed to 15.
D. Both a and b now store fee in their instance __dict__.
E. a now has its own instance attribute fee equal to 15.
Correct answers: A and E
Explanation: Assigning through one instance does not change the class variable here. a.fee += 5 creates or updates a’s own fee, while b continues to use the class attribute because it has no instance attribute of that name.
This tests the difference between class variables and instance variables. When Python evaluates a.fee += 5, it first looks up a.fee. Since a has no instance attribute named fee, Python uses the class attribute Account.fee, which is 10. It then adds 5 and assigns the result back to a.fee.
10 on the class.10 + 5 produces 15.fee into a’s instance namespace.b still has no instance fee, so it keeps reading the class value 10.The common mistake is assuming that changing an attribute through one instance automatically changes the class attribute for every object.
a.fee, not Account.fee.b was never assigned a fee attribute.a gets a new instance-level fee; b still relies on class lookup.Topic: Section 4: Object-Oriented Programming
A developer is debugging unexpected shared state. Each Session object should keep its own mutable list of events.
class Session:
events = []
def __init__(self, user):
self.user = user
def add(self, name):
self.events.append(name)
After creating a = Session('ann') and b = Session('bob'), calling a.add('login') also makes b.events contain 'login'. Which TWO changes independently fix this bug?
Options:
A. In __init__, assign self.__class__.events = [].
B. Change add() to use Session.events.append(name).
C. Replace __init__ with def __init__(self, user, events=[]): self.user = user; self.events = events.
D. Replace __init__ with def __init__(self, user, events=None): self.user = user; self.events = [] if events is None else list(events).
E. Replace events = [] with events = list().
F. Add self.events = [] inside __init__.
Correct answers: D and F
Explanation: events = [] in the class body creates one mutable list shared by all instances. A correct fix is to give each object its own self.events list during construction, either directly or with a None-sentinel constructor pattern.
This bug comes from the difference between class variables and instance variables. Python looks for self.events on the instance first; if it is not there, it falls back to the class. In the original code, events exists only on the class, so every Session object appends to the same shared list.
A safe repair is to assign an instance attribute during __init__, such as self.events = []. The longer events=None version is also safe because it creates a new list for each constructor call and can copy any provided starting values with list(events).
Changing only the class attribute syntax does not help, and using a mutable default argument would recreate the same sharing problem in the constructor.
Session.events.append(name) makes the shared class list explicit, so all instances still use one list.events = list() is just another way to create one class-level list object.self.__class__.events = [] rebinds the class attribute for every instance, so it remains shared.events=[] as a default parameter creates one shared list at function definition time.Topic: Section 4: Object-Oriented Programming
A developer adds print statements to trace initialization in a small class hierarchy. What is the exact output of this program?
class Part:
def __init__(self):
print("part", end="-")
class Device:
def __init__(self):
print("device", end="-")
self.part = Part()
class Phone(Device):
def __init__(self):
print("phone", end="-")
super().__init__()
print("done", end="")
Phone()
Options:
A. phone-part-device-done
B. phone-device-done-part
C. device-phone-part-done
D. phone-device-part-done
Best answer: D
Explanation: Object creation begins with the constructor of the class being instantiated, so Phone.__init__ runs first. The parent constructor runs only when super().__init__() is reached, and that parent constructor creates the Part object before execution returns to Phone.__init__.
Python starts initialization with the __init__ method of the class you instantiate. Here, Phone() calls Phone.__init__, so phone- is printed first. When super().__init__() executes, control moves to Device.__init__, which prints device- and then creates a Part instance. Creating that object immediately runs Part.__init__, printing part-. After Device.__init__ finishes, execution returns to Phone.__init__, which prints done.
Phone.__init__Device.__init__ via super()Part.__init__ via Part()Phone.__init__The key takeaway is that inherited and nested constructors run only where the code explicitly calls them.
device is wrong because instantiating Phone enters Phone.__init__ first.part before device is wrong because Device.__init__ prints before it creates the Part object.done before part is wrong because the final print happens only after super().__init__() fully completes.Topic: Section 4: Object-Oriented Programming
A package contains these files:
devices/
__init__.py
base.py
sensors.py
main.py
# devices/base.py
class Device:
def __init__(self, name):
self.name = name.strip()
self._events = []
def status(self):
return f"{self.name}:{len(self._events)}"
# devices/sensors.py
from devices.base import Device
class Sensor(Device):
def __init__(self, name, unit):
self.unit = unit
# main.py
from devices.sensors import Sensor
s = Sensor(" temp ", "C")
print(s.status())
Running main.py raises an AttributeError. Which change best fixes the code while preserving initialization expected by the inherited status() method?
Options:
A. Set self.name = name and self.unit = unit in Sensor.__init__.
B. Replace from devices.base import Device with import devices.base.
C. Move Device into devices/__init__.py and import it from there.
D. Call super().__init__(name) in Sensor.__init__, then set self.unit.
Best answer: D
Explanation: When a subclass defines its own __init__, Python does not automatically run the parent class constructor. Here, status() depends on attributes created in Device.__init__, so Sensor must call super().__init__(name) before adding its own state.
The core concept is superclass initialization. If a subclass overrides __init__, any setup done by the inherited class is skipped unless the subclass explicitly calls the parent constructor.
In this package, Device.__init__ creates both name and _events, and status() uses them. Because Sensor.__init__ only assigns unit, the Sensor instance is missing state that inherited behavior expects.
class Sensor(Device):
def __init__(self, name, unit):
super().__init__(name)
self.unit = unit
That pattern preserves base-class initialization and then adds subclass-specific attributes. Import style or package layout does not fix missing object state, and manually copying only part of the base initialization is incomplete.
name and unit still skips _events, so inherited status() lacks required state.__init__.py changes package organization, not constructor behavior.Topic: Section 4: Object-Oriented Programming
A developer is debugging a class used to store per-device settings. After calling configure(5), the value should belong only to that object and appear in the object’s __dict__, not in the class.
class Sensor:
def __init__(self, name):
self.name = name
def configure(self, level):
# replace this line
level = level
s = Sensor("A")
s.configure(5)
Which replacement line inside configure correctly declares and initializes an instance variable?
Options:
A. Sensor.level = level
B. self.level == level
C. self.level = level
D. level = self.level
Best answer: C
Explanation: Instance variables are created by assigning to an attribute of a specific object, usually through self inside a method. Using self.level = level stores level on that Sensor instance, so each object can keep its own value.
The core idea is that an instance variable is attached to one object, not to the class itself. Inside an instance method, self refers to the current object, so self.level = level both declares and initializes the attribute for that specific instance.
After that assignment, the new attribute appears in the instance namespace, such as s.__dict__. By contrast, assigning to Sensor.level creates or updates a class variable shared through the class. Writing level = self.level tries to read an attribute instead of creating it, and self.level == level is only a comparison, not an assignment.
A good rule is: use self.attribute = value in methods when you want per-object state.
Sensor.level = level writes to the class, so the value is shared as a class variable rather than stored per instance.level = self.level reverses the assignment and attempts to read self.level before initializing it.self.level == level compares values and does not create or store any attribute.Topic: Section 4: Object-Oriented Programming
In a GUI toolkit, these reusable classes already exist:
class Widget: pass
class Clickable: pass
class Draggable: pass
A developer must add:
Label, using single inheritance from Widget onlyIconButton, using multiple inheritance from Widget and ClickableWhich class definitions satisfy the requirement? Select TWO.
Options:
A. class Label(Widget, Clickable): pass
B. class Label(Clickable): pass
C. class IconButton(Widget, Draggable): pass
D. class Label(Widget): pass
E. class IconButton(Widget, Clickable): pass
F. class IconButton(Widget): pass
Correct answers: D and E
Explanation: Single inheritance means one base class in the class header, while multiple inheritance means two or more. Label therefore needs only Widget, and IconButton must list both Widget and Clickable.
In Python, the inheritance structure is defined in the class header inside parentheses. If a class has one parent, it uses single inheritance; if it has two or more parents, it uses multiple inheritance. Here, Label must inherit from only Widget, so its header should contain exactly that one base class. IconButton must combine behavior from Widget and Clickable, so both base classes must appear in its header.
Any definition that adds an extra parent to Label, omits Clickable from IconButton, or replaces a required base with another class does not match the stated hierarchy. The key is to check both the number of parents and whether they are the correct parents.
Label inherit from Widget and Clickable fails because Label was required to use single inheritance only.IconButton inherit only from Widget fails because it omits Clickable.IconButton inherit from Widget and Draggable uses multiple inheritance, but with the wrong second base class.Label inherit only from Clickable has single inheritance, but from the wrong parent.Topic: Section 4: Object-Oriented Programming
A developer is adding diagnostic logging and must record the runtime class name of pet as the string Dog.
class Animal:
pass
class Dog(Animal):
pass
pet = Dog()
Which TWO expressions return the string Dog? Select TWO.
Options:
A. pet.__class__.__name__
B. type(pet).__name__
C. type(Dog).__name__
D. Dog.__module__
E. Dog.__bases__[0].__name__
F. pet.__name__
Correct answers: A and B
Explanation: A class object stores its class name in __name__. For an instance like pet, you must first get its class with __class__ or type(), then read __name__.
In Python, __name__ is metadata stored on the class object itself. That means you do not normally read a class name directly from an instance; you first obtain the instance’s class, then access that class object’s __name__ attribute. Here, both pet.__class__ and type(pet) refer to the Dog class, so appending .__name__ returns 'Dog'.
__module__ identifies where the class was defined.__bases__ lists parent classes.type(Dog) refers to the metaclass of Dog, which is type.The key idea is to apply __name__ to the correct class object, not to unrelated metadata.
pet.__name__ fails because ordinary instances do not expose their class name that way.Dog.__module__ returns the module name, not the class name.Dog.__bases__[0].__name__ returns Animal, because it inspects the first base class.type(Dog).__name__ returns type, because classes are instances of the metaclass type.Topic: Section 4: Object-Oriented Programming
A team is writing a Book class. edition must be a class variable shared by all objects, and each object must store its own title and pages. After calling Book("PCAP Guide", 250), the instance __dict__ should be {'title': 'PCAP Guide', 'pages': 250}.
Which constructor signature should replace the comment?
class Book:
edition = "3rd"
# replace this line
Options:
A. def __init__(self, title, pages):
B. def __init__(self):
C. def __init__(title, pages):
D. def __init__(self, edition, title, pages):
Best answer: A
Explanation: A constructor for an instance must include self and parameters only for the data each object needs at creation time. Here, the per-object data is title and pages, while edition is shared by the class and should not be passed to every new object.
__init__ is the initializer for a new instance, so its signature should match the values required to build that instance. In this scenario, each Book object needs its own title and pages, because those are the values that should appear in the instance __dict__. Python also passes the new object automatically to instance methods, so self must be the first parameter.
Because edition is shared across all Book objects, it belongs as a class variable, not as a required constructor argument. A signature with self, title, and pages matches both the call Book("PCAP Guide", 250) and the desired initialized state. The closest wrong idea is requiring edition, which incorrectly turns shared class data into per-instance input.
self omits the instance reference that Python supplies automatically to object methods.edition wrongly treats shared class data as if every object must receive it during construction.self cannot accept the two values passed when creating the object.Use the PCAP-31-03 Practice Test page for the full IT Mastery route, mixed-topic practice, timed mock exams, explanations, and web/mobile app access.
Try PCAP-31-03 on Web View PCAP-31-03 Practice Test
Read the PCAP-31-03 Cheat Sheet on Tech Exam Lexicon, then return to IT Mastery for timed practice.