A checking account’s steadiness ought to by no means go beneath zero. If steadiness is a public discipline, each line of code that may see an account may break that rule, and discovering the road that did means looking out the entire program. If the steadiness is non-public and adjustments solely by means of deposit and withdraw, the rule is checked in a single place, for each caller. That’s encapsulation.
This information explains encapsulation as an object-oriented idea, with the identical examples in C++, Java and Python: how non-public state and public strategies shield an object’s guidelines, how a getter can quietly hand these guidelines away, why getters and setters alone aren’t encapsulation, and the way Python achieves the identical factor by conference. Each program was run for this text on Ubuntu 24.04 with GCC 13.3 and Clang 18.1.3 (-std=c++17 -Wall -Wextra -pedantic -Werror), OpenJDK 21 (javac -Xlint:all -Werror) and Python 3.11, 3.12 and three.13 (-W error), and each output beneath is captured verbatim. The code is in a GitHub repository whose construct repeats these checks on every commit.
The Brief Reply
What’s encapsulation in OOP? Bundling an object’s information with the strategies that function on it, and permitting outdoors code to alter that information solely by means of these strategies. The strategies implement the item’s guidelines, so each object stays legitimate nonetheless callers use these strategies.
Is encapsulation the identical as information hiding? No, however they’re intently associated. Information hiding (making fields non-public) is the principle device encapsulation makes use of. Encapsulation is the purpose: one class owns its information and decides how that information might change.
Are getters and setters encapsulation? Not by themselves. A setter that accepts any worth protects nothing a public discipline wouldn’t. Encapsulation comes from strategies that implement guidelines, equivalent to withdraw refusing to overdraw an account.
What Is Encapsulation?
Encapsulation in object-oriented programming is the follow of holding an object’s state and the code that adjustments it collectively in a single class, and proscribing direct entry to that state from outdoors. Exterior code works with the item solely by means of its public strategies, which verify each change in opposition to the item’s guidelines (its invariants), so code that makes use of the item by means of that interface can not put it into an invalid state.
An invariant is a situation that holds for each object of a category each time outdoors code can observe it. For the account beneath, the invariants are that the steadiness isn’t destructive and that the historical past of transactions provides as much as the steadiness. Encapsulation is what makes an invariant enforceable: if solely the category’s personal code can change the steadiness, then solely the category’s personal code must be checked.
Encapsulation has three elements:
| Half | What it means | Within the account instance |
|---|---|---|
| Bundle | Information and the operations on it reside in a single class | steadiness and historical past sit subsequent to deposit and withdraw |
| Prohibit | Exterior code can not attain the info immediately | The fields are non-public (C++, Java) or marked non-public by conference (Python) |
| Implement | Each change goes by means of a way that checks the foundations | withdraw refuses quantities above the steadiness and quantities beneath 1 |
The concept predates object-oriented languages. David Parnas’s 1972 paper “On the Standards To Be Utilized in Decomposing Methods into Modules” argued that every module ought to conceal a design determination behind an interface, so the choice can change with out breaking the code that makes use of it. Courses with non-public members are essentially the most acquainted means of making use of that precept at the moment.
Encapsulation in C++, Java and Python
The identical account class in all three languages. The steadiness can change solely by means of deposit and withdraw, and the historical past is on the market to callers solely as one thing they can’t modify:
C++:
// account.cpp - encapsulation: the steadiness can change solely by means of
// deposit() and withdraw(), which by no means let it go beneath zero.
#embrace <iostream>
#embrace <stdexcept>
#embrace <string>
#embrace <utility>
#embrace <vector>
class Account {
public:
Account(std::string proprietor, lengthy opening) : owner_(std::transfer(proprietor)) {
deposit(opening);
}
void deposit(lengthy quantity) {
require_positive(quantity);
balance_ += quantity;
history_.push_back(quantity);
}
void withdraw(lengthy quantity) {
require_positive(quantity);
if (quantity > balance_) {
throw std::invalid_argument("inadequate funds: steadiness " + std::to_string(balance_) +
", requested " + std::to_string(quantity));
}
balance_ -= quantity;
history_.push_back(-amount);
}
const std::string& proprietor() const { return owner_; }
lengthy steadiness() const { return balance_; }
const std::vector<lengthy>& historical past() const { return history_; } // read-only to callers
non-public:
static void require_positive(lengthy quantity) {
if (quantity <= 0) {
throw std::invalid_argument("quantity should be optimistic: " + std::to_string(quantity));
}
}
std::string owner_;
lengthy balance_ = 0;
std::vector<lengthy> history_;
};
int essential() {
Account acct("Ada", 100);
std::cout << acct.proprietor() << " opened with " << acct.steadiness() << 'n';
acct.deposit(50);
std::cout << "deposit 50 -> steadiness " << acct.steadiness() << 'n';
acct.withdraw(30);
std::cout << "withdraw 30 -> steadiness " << acct.steadiness() << 'n';
for (lengthy quantity : {500L, -5L}) {
attempt {
acct.withdraw(quantity);
} catch (const std::invalid_argument& e) {
std::cout << "error: " << e.what() << 'n';
}
}
std::cout << "steadiness " << acct.steadiness() << 'n';
std::cout << "historical past:";
for (lengthy t : acct.historical past()) {
std::cout << ' ' << (t > 0 ? "+" : "") << t;
}
std::cout << 'n';
// acct.balance_ = 1000000; // doesn't compile: balance_ is non-public
}
Java:
// Account.java - encapsulation: the steadiness can change solely by means of
// deposit() and withdraw(), which by no means let it go beneath zero.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Record;
public last class Account {
non-public last String proprietor;
non-public lengthy steadiness = 0;
non-public last Record<Lengthy> historical past = new ArrayList<>();
public Account(String proprietor, lengthy opening) {
this.proprietor = proprietor;
deposit(opening);
}
public void deposit(lengthy quantity) {
requirePositive(quantity);
steadiness += quantity;
historical past.add(quantity);
}
public void withdraw(lengthy quantity) {
requirePositive(quantity);
if (quantity > steadiness) {
throw new IllegalArgumentException(
"inadequate funds: steadiness " + steadiness + ", requested " + quantity);
}
steadiness -= quantity;
historical past.add(-amount);
}
public String proprietor() { return proprietor; }
public lengthy steadiness() { return steadiness; }
public Record<Lengthy> historical past() { return Collections.unmodifiableList(historical past); } // read-only view
non-public static void requirePositive(lengthy quantity) {
if (quantity <= 0) {
throw new IllegalArgumentException("quantity should be optimistic: " + quantity);
}
}
public static void essential(String[] args) {
Account acct = new Account("Ada", 100);
System.out.println(acct.proprietor() + " opened with " + acct.steadiness());
acct.deposit(50);
System.out.println("deposit 50 -> steadiness " + acct.steadiness());
acct.withdraw(30);
System.out.println("withdraw 30 -> steadiness " + acct.steadiness());
for (lengthy quantity : new lengthy[] {500, -5}) {
attempt {
acct.withdraw(quantity);
} catch (IllegalArgumentException e) {
System.out.println("error: " + e.getMessage());
}
}
System.out.println("steadiness " + acct.steadiness());
StringBuilder line = new StringBuilder("historical past:");
for (lengthy t : acct.historical past()) {
line.append(' ').append(t > 0 ? "+" : "").append(t);
}
System.out.println(line);
}
}
Python:
"""account.py - encapsulation: the steadiness can change solely by means of
deposit() and withdraw(), which by no means let it go beneath zero."""
class Account:
def __init__(self, proprietor: str, opening: int) -> None:
self._owner = proprietor
self._balance = 0
self._history: checklist[int] = []
self.deposit(opening)
def deposit(self, quantity: int) -> None:
self._require_positive(quantity)
self._balance += quantity
self._history.append(quantity)
def withdraw(self, quantity: int) -> None:
self._require_positive(quantity)
if quantity > self._balance:
elevate ValueError(f"inadequate funds: steadiness {self._balance}, requested {quantity}")
self._balance -= quantity
self._history.append(-amount)
@property
def proprietor(self) -> str:
return self._owner
@property
def steadiness(self) -> int:
return self._balance
@property
def historical past(self) -> tuple[int, ...]:
return tuple(self._history) # a read-only copy
@staticmethod
def _require_positive(quantity: int) -> None:
if quantity <= 0:
elevate ValueError(f"quantity should be optimistic: {quantity}")
acct = Account("Ada", 100)
print(f"{acct.proprietor} opened with {acct.steadiness}")
acct.deposit(50)
print(f"deposit 50 -> steadiness {acct.steadiness}")
acct.withdraw(30)
print(f"withdraw 30 -> steadiness {acct.steadiness}")
for quantity in (500, -5):
attempt:
acct.withdraw(quantity)
besides ValueError as e:
print(f"error: {e}")
print(f"steadiness {acct.steadiness}")
print("historical past:", " ".be a part of(f"{t:+d}" for t in acct.historical past))
Output (equivalent for all three):
Ada opened with 100
deposit 50 -> steadiness 150
withdraw 30 -> steadiness 120
error: inadequate funds: steadiness 120, requested 500
error: quantity should be optimistic: -5
steadiness 120
historical past: +100 +50 -30
The withdrawal of 500 and the withdrawal of -5 had been each rejected, and the steadiness stayed at 120 in each language. Code that makes use of the general public interface has no method to make it destructive, as a result of the interface reduces the steadiness solely by means of withdraw.
What occurs when outdoors code tries to put in writing the steadiness immediately differs by language. All three had been checked:
| Language | Assertion | Outcome |
|---|---|---|
| C++ | acct.balance_ = 1000000; |
Compile error: GCC stories 'lengthy int Account::balance_' is non-public inside this context |
| Java (from one other class) | acct.steadiness = 1000000; |
Compile error: steadiness has non-public entry in Account |
| Python | acct.steadiness = 1000000 |
AttributeError: property 'steadiness' of 'Account' object has no setter |
| Python | acct._balance = 1000000 |
Runs. The underscore marks the attribute as inside, however nothing enforces it |
The Java essential technique is inside Account solely to maintain the instance in a single file. Code inside a category can use its non-public fields, so the compile error seems when one other class tries the identical task.
Entry Management In contrast
Every language offers a distinct quantity of assist in proscribing entry:
| Side | C++ | Java | Python |
|---|---|---|---|
| Non-public to the category | non-public: |
non-public |
_name by conference; __name is renamed to _Class__name |
| Seen to subclasses | protected: |
protected (additionally seen in the identical bundle) |
No language assist; _name is the conference |
| Default for members | non-public in a class, public in a struct |
Package deal-private: seen throughout the identical bundle | Public |
| Checked when | Compile time | Compile time | Not checked; read-only properties elevate errors at run time |
| Exceptions | buddy features and lessons |
Reflection, topic to the module system | Every little thing is reachable |
Java’s personal tutorial offers a rule that works in all three languages: “Use non-public until you might have purpose to not,” and keep away from public fields aside from constants (Controlling Entry to Members of a Class). C++ describes the identical break up in its phrases: public members type the interface of a category, non-public members type its implementation (cppreference: entry specifiers). The syntax of every key phrase is roofed in lessons in object-oriented programming.
How a Getter Breaks Encapsulation
Making a discipline non-public isn’t sufficient if a way palms it again to callers. A getter that returns a reference to an inside checklist offers each caller the ability to alter that checklist, bypassing each verify the category makes. Within the subsequent program a group has a restrict of three members, enforced by add. LeakyTeam returns its non-public checklist; Group returns one thing the caller can not use to alter the group:
C++:
// leak.cpp - returning a reference to non-public information palms it to the caller.
// LeakyTeam's restrict of three members will be damaged from outdoors; Group's can not.
#embrace <cstddef>
#embrace <iostream>
#embrace <stdexcept>
#embrace <string>
#embrace <vector>
constexpr std::size_t kLimit = 3;
class LeakyTeam {
public:
void add(const std::string& title) {
if (members_.measurement() >= kLimit) throw std::length_error("group is full");
members_.push_back(title);
}
std::vector<std::string>& members() { return members_; } // the non-public vector itself
non-public:
std::vector<std::string> members_;
};
class Group {
public:
void add(const std::string& title) {
if (members_.measurement() == kLimit) throw std::length_error("group is full");
members_.push_back(title);
}
const std::vector<std::string>& members() const { return members_; } // read-only
non-public:
std::vector<std::string> members_;
};
int essential() {
LeakyTeam leaky;
for (const char* n : {"Ana", "Ben", "Cy"}) leaky.add(n);
std::cout << "leaky: added Ana, Ben, Cy -> " << leaky.members().measurement() << " membersn";
leaky.members().push_back("Dee");
leaky.members().push_back("Eve");
std::cout << "leaky: caller appended twice -> " << leaky.members().measurement()
<< " members (restrict is " << kLimit << ")n";
Group group;
for (const char* n : {"Ana", "Ben", "Cy"}) group.add(n);
std::cout << "protected: added Ana, Ben, Cy -> " << group.members().measurement() << " membersn";
// group.members().push_back("Dee"); // doesn't compile: the reference is const
std::vector<std::string> mine = group.members(); // an express copy is the caller's personal
mine.push_back("Dee");
std::cout << "protected: caller's copy has " << mine.measurement() << ", group nonetheless has "
<< group.members().measurement() << " membersn";
}
Output:
leaky: added Ana, Ben, Cy -> 3 members
leaky: caller appended twice -> 5 members (restrict is 3)
protected: added Ana, Ben, Cy -> 3 members
protected: caller's copy has 4, group nonetheless has 3 members
Java:
// Leak.java - returning the non-public checklist palms it to the caller.
// LeakyTeam's restrict of three members will be damaged from outdoors; Group's can not.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Record;
public last class Leak {
static last int LIMIT = 3;
static last class LeakyTeam {
non-public last Record<String> members = new ArrayList<>();
void add(String title) {
if (members.measurement() >= LIMIT) throw new IllegalStateException("group is full");
members.add(title);
}
Record<String> members() { return members; } // the non-public checklist itself
}
static last class Group {
non-public last Record<String> members = new ArrayList<>();
void add(String title) {
if (members.measurement() >= LIMIT) throw new IllegalStateException("group is full");
members.add(title);
}
Record<String> members() { return Collections.unmodifiableList(members); } // read-only view
}
public static void essential(String[] args) {
LeakyTeam leaky = new LeakyTeam();
for (String n : Record.of("Ana", "Ben", "Cy")) leaky.add(n);
System.out.println("leaky: added Ana, Ben, Cy -> " + leaky.members().measurement() + " members");
leaky.members().add("Dee");
leaky.members().add("Eve");
System.out.println("leaky: caller appended twice -> " + leaky.members().measurement()
+ " members (restrict is " + LIMIT + ")");
Group group = new Group();
for (String n : Record.of("Ana", "Ben", "Cy")) group.add(n);
System.out.println("protected: added Ana, Ben, Cy -> " + group.members().measurement() + " members");
attempt {
group.members().add("Dee");
} catch (UnsupportedOperationException e) {
System.out.println("protected: add rejected (UnsupportedOperationException), group nonetheless has "
+ group.members().measurement() + " members");
}
}
}
Output:
leaky: added Ana, Ben, Cy -> 3 members
leaky: caller appended twice -> 5 members (restrict is 3)
protected: added Ana, Ben, Cy -> 3 members
protected: add rejected (UnsupportedOperationException), group nonetheless has 3 members
Python:
"""leak.py - returning the non-public checklist palms it to the caller.
LeakyTeam's restrict of three members will be damaged from outdoors; Group's can not."""
LIMIT = 3
class LeakyTeam:
def __init__(self) -> None:
self._members: checklist[str] = []
def add(self, title: str) -> None:
if len(self._members) >= LIMIT:
elevate ValueError("group is full")
self._members.append(title)
@property
def members(self) -> checklist[str]:
return self._members # the non-public checklist itself
class Group:
def __init__(self) -> None:
self._members: checklist[str] = []
def add(self, title: str) -> None:
if len(self._members) >= LIMIT:
elevate ValueError("group is full")
self._members.append(title)
@property
def members(self) -> tuple[str, ...]:
return tuple(self._members) # a read-only copy
leaky = LeakyTeam()
for n in ("Ana", "Ben", "Cy"):
leaky.add(n)
print(f"leaky: added Ana, Ben, Cy -> {len(leaky.members)} members")
leaky.members.append("Dee")
leaky.members.append("Eve")
print(f"leaky: caller appended twice -> {len(leaky.members)} members (restrict is {LIMIT})")
group = Group()
for n in ("Ana", "Ben", "Cy"):
group.add(n)
print(f"protected: added Ana, Ben, Cy -> {len(group.members)} members")
attempt:
group.members.append("Dee")
besides AttributeError:
print(f"protected: append rejected (AttributeError), group nonetheless has {len(group.members)} members")
Output:
leaky: added Ana, Ben, Cy -> 3 members
leaky: caller appended twice -> 5 members (restrict is 3)
protected: added Ana, Ben, Cy -> 3 members
protected: append rejected (AttributeError), group nonetheless has 3 members
The primary two traces are the identical in each language: two calls to append or push_back on the returned checklist took the leaky group to 5 members, previous a restrict that add checks on each name. The category’s rule held just for callers who selected to make use of add.
The protected variations shield the checklist in several methods, which is why the final line differs:
- C++ returns a
constreference. Callingpush_backon it doesn’t compile, so the try is a remark in this system; a caller that wishes to alter the checklist should copy it first, and altering the copy leaves the group alone. - Java returns
Collections.unmodifiableList(members), a read-only view: it displays later adjustments to the group, and any try to change it throwsUnsupportedOperationException(theunmodifiableListentry within the JavaCollectionsdocumentation). - Python returns a tuple, a replica that has no
appendtechnique, so the try raisesAttributeError.
| Want | C++ | Java | Python |
|---|---|---|---|
| Learn-only entry to the reside information | Return const std::vector<T>& (or std::span<const T> in C++20) |
Collections.unmodifiableList(checklist) |
No read-only checklist sort; return a tuple copy, or an iterator |
| An unbiased snapshot | Return std::vector<T> by worth |
Record.copyOf(checklist) |
tuple(self._items) or checklist(self._items) |
Two cautions apply to the read-only choices. A C++ const reference or a Java view stays related to the item, so it adjustments when the item adjustments, and a C++ reference should not be used after its object is destroyed. And “read-only” covers the container, not its parts: if the weather are mutable objects, callers can nonetheless change them by means of the view. The identical leak works within the different route, too. A constructor that shops a caller’s checklist with out copying it shares that checklist with the caller, who can change it later; copying mutable arguments on the way in which in closes that hole.
Getters and Setters Are Not Encapsulation
A category with non-public fields and a public getter and setter for each is encapsulated in type solely. Evaluate two methods of taking 30 out of an account:
acct.setBalance(acct.getBalance() - 30)reads the info, makes the choice outdoors the category, and writes the end result again. The rule {that a} steadiness might not go destructive now needs to be repeated by each caller, and any caller can neglect it.acct.withdraw(30)asks the item to do the job. The rule lives in a single technique, and the account above has nosetBalancein any respect.
The second type is usually summed up as “inform, don’t ask”: inform an object what to do as an alternative of asking for its information and appearing on it your self. A helpful take a look at when designing a category is to put in writing the operations callers want (withdraw, add, rename) earlier than writing any accessor, and so as to add a getter solely when a caller genuinely wants the worth. Many lessons want fewer setters than their fields counsel; the account wants none.
Not each class wants encapsulation. A kind with no guidelines to guard, equivalent to a degree with an x and a y which will take any values, beneficial properties nothing from non-public fields and accessor pairs. C++ makes use of a struct with public members for this, Java has document lessons (since Java 16), and Python has @dataclass. Information and dataclasses can nonetheless validate their values when they’re constructed, and a document’s fields can’t be reassigned afterwards.
Encapsulation in Python: Properties and Conventions
Python has no non-public key phrase. Its documentation says plainly that “non-public” occasion variables “don’t exist in Python”, and {that a} main underscore marks a reputation as a private a part of the API, an implementation element which will change with out discover (Python tutorial: non-public variables). Encapsulation in Python is subsequently a contract between the category and its callers reasonably than a rule the interpreter enforces, and the language gives properties to make the general public aspect of that contract appear like plain attributes:
"""properties.py - a property provides validation behind an attribute title,
so callers hold writing c.radius. The underscore is simply a conference."""
import math
class Circle:
def __init__(self, radius: float) -> None:
self.radius = radius # goes by means of the setter beneath
@property
def radius(self) -> float:
return self._radius
@radius.setter
def radius(self, worth: float) -> None:
if worth < 0:
elevate ValueError(f"radius should be non-negative: {worth}")
self._radius = worth
@property
def space(self) -> float:
return math.pi * self._radius ** 2
c = Circle(2.0)
print(f"radius {c.radius}, space {c.space:.2f}")
attempt:
c.radius = -1
besides ValueError as e:
print(f"error: {e}")
print(f"radius nonetheless {c.radius}")
c._radius = -1 # nothing stops this: Python trusts the caller to respect the underscore
print(f"after c._radius = -1: radius {c.radius}")
Output:
radius 2.0, space 12.57
error: radius should be non-negative: -1
radius nonetheless 2.0
after c._radius = -1: radius -1
Callers write c.radius = -1, precisely as if radius had been an atypical attribute, and the setter rejects it. For this reason Python code normally begins with public attributes and no getters or setters: if validation turns into crucial later, a property will be added with out altering any caller. The space property reveals the opposite use, a computed worth that callers learn like a discipline.
The final line reveals the restrict. Writing c._radius immediately skipped the setter and left the circle with a destructive radius. A double underscore (__radius) would rename the attribute to _Circle__radius, however that exists to stop title clashes in subclasses, and the tutorial notes it’s “nonetheless attainable to entry or modify a variable that’s thought of non-public”. In Python, the underscore is the boundary, and respecting it’s the caller’s accountability. Linters equivalent to Pylint flag entry to protected members from outdoors a category, which helps a group implement the conference.
Encapsulation vs Abstraction vs Information Hiding
The three phrases are sometimes used interchangeably. They describe various things:
| Side | Encapsulation | Abstraction | Information hiding |
|---|---|---|---|
| Query it solutions | Who might change this information, and the way? | What does this kind do, ignoring how? | Which members can outdoors code see? |
| Most important device | A category that owns its information and checks each change | Interfaces, summary lessons, well-named strategies | Entry modifiers (non-public, protected), or conventions |
| Within the account instance | withdraw is the one method to scale back the steadiness |
Callers consider “an account”, not a lengthy and a vector |
balance_ is non-public |
| What it protects | The article’s invariants | Callers from implementation particulars | The fields from direct entry |
The three work collectively. Abstraction decides what the general public interface must be, encapsulation retains the info behind that interface legitimate, and information hiding is how a language enforces the boundary between them.
Key Takeaways
- Encapsulation retains information and the code that adjustments it collectively, and lets outdoors code change the info solely by means of strategies that verify it. The account’s steadiness stayed at 120 after an overdraft and a destructive quantity had been each rejected.
- Non-public fields are the device, not the purpose. The purpose is that the category’s guidelines (its invariants) at all times maintain.
- A getter can leak non-public information. Returning the inner checklist let callers take a three-member group to 5; a
constreference, an unmodifiable view or a replica prevented it. - Getters and setters don’t add encapsulation by themselves. Strategies equivalent to
withdrawthat implement guidelines do. - Python encapsulates by conference. Properties validate adjustments with out altering the caller’s syntax, and a number one underscore marks internals, however nothing prevents entry to them.
Conclusion
Encapsulation turns a category’s guidelines from recommendation into ensures. When the info can change solely by means of strategies that verify it, an invalid object can’t be created accidentally, and when one does flip up, the code that induced it’s confined to a single class. C++ and Java implement the boundary at compile time; Python leaves it to conference and provides properties for the instances that want checks. In all three, the helpful query is similar: what should at all times be true about this object, and which strategies are allowed to alter it?
For the C++-specific element, together with buddy declarations, a category that owns a useful resource, and whether or not non-public prices something at run time, see encapsulation in C++. The remainder of the object-oriented sequence builds on the identical concepts: objects covers id and copying, which explains why returning a reference shares information, and inheritance defines new lessons from current ones, the place protected members determine how far the boundary opens. Polymorphism builds on the identical boundary, letting callers use any class that retains the interface’s guarantees. The overview of object-oriented programming reveals encapsulation working with the opposite pillars in a single program, and extra matters are collected within the OOP part.
Supply Code and Checks
All packages on this web page are within the oop/encapsulation-concepts listing of the MYCPLUS C++ examples repository.
Construct and take a look at:
bash assessments/run_tests.sh g++
What the construct checks. On Linux, with GCC and with Clang, it compiles the C++ packages with -Wall -Wextra -pedantic -Werror, compiles the Java packages with javac -Xlint:all -Werror on Java 21, runs the Python packages with -W error on Python 3.12, and checks that each program prints precisely the output proven on this web page, with a separate anticipated output per language for the leaking-getter instance. Python 3.11 and three.13 had been checked regionally and produced the identical output. The compile errors and the AttributeError quoted within the desk had been produced by quickly including the assignments; they aren’t a part of the automated construct.

