B3.1.5 · Fundamentals of OOP for a single class
Encapsulation & information hiding
Why a class hides its own data, and how the methods around it are the only door in.
Explainer
Think of a car’s dashboard. You press the accelerator and the brake; you never reach under the bonnet and spin the engine by hand. The dashboard is the only door in, and everything behind it is off limits. That’s exactly what encapsulation gives a class.
Encapsulation is the practice of hiding an object’s internal implementation details and exposing only what the rest of the program needs, through a defined set of methods.
A class does this by marking its attributes private and giving the outside world public methods to read or change them, never direct access to the variable itself. That’s information hiding: the data is still there, it’s just not reachable except through the door the class chose to leave open.
The two ideas are often tested together but they answer different questions. Information hiding is what is hidden (the attributes). Encapsulation is the whole mechanism: bundling the data with the methods that are allowed to touch it, so the class itself can guarantee its own rules are never broken.
Python does not actually enforce any of this. Java has four real access modifiers, checked by the compiler: private (this object only), protected (this object and anything that inherits it, covered properly once inheritance is taught), public (anywhere), and a fourth, unnamed default level when no keyword is written at all (accessible anywhere in the same package). Python has no keywords and no equivalent to that default package-level modifier, only a naming convention with three levels instead: no underscore is public (the default), a single leading underscore (_spaces_free) signals "internal use only" but is purely a hint, nothing stops outside code reaching it, and a double leading underscore (self.__spaces_free, used below) triggers name mangling: Python renames it internally to _CarPark__spaces_free, so a stray typo or an accidental reference can no longer stumble onto it, though anyone who deliberately goes looking for _CarPark__spaces_free by name can still find it. Same idea, very different level of enforcement, and IB expects you to know which is which.
Accessor and mutator (also called getter and setter) are the formal names for the public methods that do the reading and writing a private attribute can no longer do directly. An accessor returns a private value without changing it (getSpacesFree() below). A mutator changes one, but unlike a plain "setter" that just overwrites a value, vehicleEntered() below is a mutator that VALIDATES first, this is exactly what separates real encapsulation from simply hiding a variable: the mutator is where the class's own rules actually get enforced.
Worked example
A CarPark that protects its own space count
Holloway Lane Car Park has 40 bays. The number of free spaces must never fall below zero, and must never climb above the capacity of the site either. If that count were public, any line of code anywhere in the program could set it to whatever it wanted, and both rules would depend on every programmer remembering to check them. Encapsulation moves that responsibility inside the class itself.
Click an attribute or method to see which ones connect.
# spaces_free is PRIVATE: outside code cannot write park.spaces_free directly (leading __) class CarPark: def __init__(self, site_name, capacity): self.site_name = site_name self.__capacity = capacity self.__spaces_free = capacity def get_spaces_free(self): return self.__spaces_free def vehicle_entered(self): if self.__spaces_free > 0: self.__spaces_free -= 1 return True return False # rule enforced HERE, once, not everywhere spaces_free is used
// spacesFree is PRIVATE: outside code cannot write park.spacesFree directly public class CarPark { private int spacesFree; private int capacity; public int getSpacesFree() { return spacesFree; } public boolean vehicleEntered() { if (spacesFree > 0) { spacesFree--; return true; } return false; // rule enforced HERE, once, not everywhere spacesFree is used } }
getSpacesFree() is the accessor, it only reads. vehicleEntered() is the mutator, and every path that changes spacesFree runs through it. That single choke point is what makes the “never below zero” rule actually reliable.
What is self.__spaces_free (Python), or plain spacesFree inside a Java method? It’s not one shared number. It’s the free-space count belonging to this particular car park. Create two sites, north = CarPark("North", 40) and south = CarPark("South", 120), and each gets its own private copy: filling north never touches south’s count.
What does north.reserve(12) actually do? It refers to north’s own free-space count, reduced by 12. Which object (north) stays fixed for the whole call; the count (12) is the part that changes each time you call it. Inside the method, that fixed object is exactly what self.__spaces_free (or spacesFree in Java) refers to, never any other site’s.
Making spacesFree private is information hiding on its own. It only becomes encapsulation once vehicleEntered() and reserve() exist as the sole route in, so the data and the rule that protects it are bundled into one unit.
Try it yourself
Watch encapsulation actually refuse you
The CarPark class from above, live and editable. The last line is commented out on purpose: uncomment it and press Run to see Python genuinely block direct access to a private attribute, rather than just discouraging it by convention.
Running in offline fallback mode. The full Python engine couldn't load (blocked school network, or no internet), so this console switched automatically to a small hand-written engine. It runs this page's scenario, including private attributes and name mangling, but it isn't full Python: one visible difference is that a whole-number float prints without its trailing .0.
Try north.reserve(100) on a site with 28 spaces left and watch the mutator refuse it. Try north.site_name = "Somewhere else" and see that a public attribute has no such protection. Then try reaching the private count the way the explainer described, as north._CarPark__spaces_free, and notice it works: Python hides the attribute from accidents, not from someone deliberately going around the interface.
Check your understanding
Why does CarPark make spacesFree private instead of public?
Common mistake
B3.1.5RecurringCandidates who clearly know the word encapsulation still lose marks for an incomplete definition. The mark scheme wants the full idea: hiding internal detail and exposing only what’s necessary, not just a one-line mention that variables are private.
On code-based questions asking how encapsulation has been used in a given program, the weaker answers restate that the class has private variables without saying which method controls them or why. That’s a description too generic to earn full marks.
Answer frame that scores: name the private attribute → name the specific accessor/mutator that touches it → state the rule that method enforces.
What a weak answer looks like: “spacesFree is private, which makes the class more secure.” True, but it names nothing specific: no attribute-to-method link, no rule being enforced. This is the “generic response” pattern markers reported losing marks.
What it should say: “spacesFree is private, so only getSpacesFree(), vehicleEntered() and reserve() can access it. vehicleEntered() checks spacesFree > 0 before subtracting, so it can guarantee the count never drops below zero, a guarantee that would be impossible if spacesFree were public.” Attribute named, method named, rule stated.
Explainer
The advantage examiners actually want stated
“Outline one advantage of encapsulation” questions are answered well most often, but only when the advantage is tied to a consequence, not left abstract.
Real example: a multi-site parking app where dozens of files across the codebase all touch park.spacesFree directly.
Advantage
- Simple to write (no method call needed, just read or overwrite the variable directly)
Disadvantage
- No shared rule can be guaranteed (any one of those dozens of files could push spacesFree below zero or above capacity by mistake, and there is no single place to catch it)
Real example: the same app, but every file calls park.vehicleEntered() instead of touching spacesFree directly.
Advantage
- The rule is enforced in exactly one place (it prevents any part of the program from putting the object into an invalid state, because every change has to go through a method that checks it first)
Disadvantage
- More code to write upfront (every attribute that needs outside access needs its own accessor and/or mutator method, not just a bare variable)
Linking question
How this connects to the rest of the syllabus
The IB syllabus is deliberately woven together, not a stack of isolated topics. Here's how encapsulation connects specifically to A3 (networks), worth thinking through, not just memorising: it's the kind of synthesis a Paper 1/2 question can genuinely ask for.
How can the principles of encapsulation and information hiding be applied to secure network communication? (The same idea, hide the internal state, expose only a controlled interface, is exactly how a secure network service works: nothing outside it can reach its internal data directly, only through the specific requests it chooses to accept.)
Revision questions
Past-paper style. Write an answer, then reveal the marking points and mark yourself.
[1]Define the term encapsulation.
Marking points
评分要点
- Encapsulation is bundling an object's data together with the methods that operate on it, and controlling access to that data from outside the class.
- 封装是把对象的数据与作用于它的方法捆绑在一起,并控制类外部对该数据的访问。
[2]Outline one consequence of declaring the attribute spacesFree as private in the class above.
Marking points
评分要点
- Making spacesFree private means outside code cannot read or change it directly.
- Any change must go through a method of the class, so the class can keep spacesFree correct (for example never negative, never above capacity).
- 把 spacesFree 设为私有意味着外部代码不能直接读取或更改它。
- 任何改动都必须通过类的一个方法进行,所以类可以保持 spacesFree 正确(例如永不为负,永不超过容量)。
[3]A junior developer suggests removing vehicleEntered() and making spacesFree public instead, to “simplify the code.” Explain why this is a poor design decision.
Marking points
评分要点
- If spacesFree were public, any code anywhere could set it to any value, including invalid ones, so the count could no longer be trusted.
- vehicleEntered() is the controlled way to change the count: it can apply rules and keep related data consistent.
- Removing it also breaks encapsulation, coupling every caller to the internal representation, so a later change to how spaces are tracked would break all of them.
- The small saving in code is not worth losing that safety and flexibility.
- 如果 spacesFree 是公有的,任何地方的任何代码都可以把它设为任意值,包括无效值,于是这个计数不再可信。
- vehicleEntered() 是更改计数的受控方式:它可以应用规则并保持相关数据一致。
- 移除它还破坏封装,把每个调用者与内部表示耦合,于是之后改变车位跟踪方式会破坏所有这些调用者。
- 代码上的小小节省不值得失去那份安全性和灵活性。
Flashcards
Active recall for this statement’s key terms.
Keep this statement’s practice with you
Free account: unlock every revision question and flashcard for B3.1.5, and every other IB CS 2027 statement.