🔐 Encapsulation
Bundles data (variables) and methods into a single unit (class).
Prevents external interference by restricting direct access to some components.
<div class="infographic-pyencap">
<style>
.infographic-pyencap {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--c-kw: #7c3aed;
--c-self: #5c5c5c;
--pub: #1f9d55;
--prot: #b45309;
--priv: #c0392b;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
box-sizing: border-box;
max-width: 720px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pyencap[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--c-kw: #a78bfa;
--c-self: #9a968c;
--pub: #6fdc8c;
--prot: #e0a458;
--priv: #e57373;
}
.infographic-pyencap *, .infographic-pyencap *::before, .infographic-pyencap *::after { box-sizing: border-box; }
.infographic-pyencap .en-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pyencap h1 {
font-family: var(--display-font); font-weight: 600;
font-size: clamp(1.5rem, 5vw, 2.05rem); text-align: center; margin: 1.5rem 0 0.25rem; letter-spacing: -0.01em;
}
.infographic-pyencap .en-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.3rem; }
.infographic-pyencap .code-block {
font-family: var(--mono-font); font-size: 0.76rem; line-height: 1.7; white-space: pre;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 8px;
padding: 0.8rem 1rem; margin-bottom: 1rem;
}
.infographic-pyencap .code-block .kw { color: var(--c-kw); font-weight: 700; }
.infographic-pyencap .code-block .self { color: var(--c-self); }
.infographic-pyencap .code-block .pub { color: var(--pub); font-weight: 700; }
.infographic-pyencap .code-block .prot { color: var(--prot); font-weight: 700; }
.infographic-pyencap .code-block .priv { color: var(--priv); font-weight: 700; }
.infographic-pyencap .legend-row { display: flex; justify-content: center; gap: 0.8rem; margin-bottom: 1.2rem; flex-wrap: wrap; }
.infographic-pyencap .legend-item { display: flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; color: var(--ink-soft); }
.infographic-pyencap .legend-dot { width: 10px; height: 10px; border-radius: 3px; }
.infographic-pyencap .setup-row { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 1.1rem; flex-wrap: wrap; }
.infographic-pyencap .setup-row input {
font-family: var(--mono-font); font-size: 0.82rem; padding: 0.4rem 0.6rem;
border: 1.5px solid var(--line); border-radius: 5px; background: var(--paper-alt); color: var(--ink); width: 100px;
text-align: center;
}
.infographic-pyencap .setup-row input:focus { outline: none; border-color: var(--accent); }
.infographic-pyencap .setup-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700;
background: var(--accent); color: #fff; border: none; border-radius: 6px;
padding: 0.4rem 0.8rem; cursor: pointer;
}
.infographic-pyencap .try-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.4rem; margin-bottom: 1.1rem; }
@media (max-width: 560px) { .infographic-pyencap .try-grid { grid-template-columns: 1fr; } }
.infographic-pyencap .try-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.76rem; font-weight: 700; text-align: left;
background: var(--paper-alt); border: 1.5px solid var(--line); color: var(--ink);
padding: 0.42rem 0.6rem; cursor: pointer; border-radius: 6px;
}
.infographic-pyencap .try-btn.pub-b { border-left: 4px solid var(--pub); }
.infographic-pyencap .try-btn.prot-b { border-left: 4px solid var(--prot); }
.infographic-pyencap .try-btn.priv-b { border-left: 4px solid var(--priv); }
.infographic-pyencap .device {
--ed-bg: #1e1e1e; --ed-bar: #2d2d2d; --ed-text: #d4d4d4; --ed-soft: #8a8a8a; --ed-ok: #6fdc8c; --ed-err: #ff6b6b;
background: var(--ed-bg); border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
}
.infographic-pyencap .device-bar {
display: flex; align-items: center; gap: 0.4rem;
background: var(--ed-bar); padding: 0.5rem 0.7rem; border-bottom: 1px solid #3a3a3a;
font-size: 0.72rem; color: var(--ed-soft); font-weight: 600;
}
.infographic-pyencap .device-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: #5a5a5a; }
.infographic-pyencap .device-bar .dot:nth-child(1) { background: #ff5f56; }
.infographic-pyencap .device-body { padding: 0.75rem 0.9rem; font-family: var(--mono-font); font-size: 0.78rem; color: var(--ed-text); min-height: 60px; max-height: 190px; overflow-y: auto; }
.infographic-pyencap .log-line { display: flex; gap: 0.5rem; line-height: 1.7; opacity: 0; animation: en-fadein 0.25s ease forwards; flex-wrap: wrap; }
@keyframes en-fadein { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: translateY(0); } }
.infographic-pyencap .log-line .p { color: var(--ed-ok); font-weight: 700; }
.infographic-pyencap .log-line.err .out { color: var(--ed-err); font-weight: 700; }
.infographic-pyencap .log-line.ok .out { color: var(--ed-ok); font-weight: 700; }
.infographic-pyencap .log-line.warn .out { color: #e0a458; font-weight: 700; }
.infographic-pyencap .log-line .out { color: #fff; }
.infographic-pyencap .log-empty { color: var(--ed-soft); }
.infographic-pyencap .reset-row { display: flex; justify-content: center; margin-top: 0.9rem; }
.infographic-pyencap .reset-btn {
font: inherit; font-size: 0.78rem; font-weight: 600; color: var(--ink-soft);
background: none; border: 1px solid var(--line); padding: 0.35rem 0.7rem; cursor: pointer; border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.infographic-pyencap *, .infographic-pyencap *::before, .infographic-pyencap *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="en-wrap">
<div class="code-block"><span class="kw">class</span> BankAccount:
<span class="kw">def</span> __init__(<span class="self">self</span>, owner, balance):
<span class="self">self</span>.<span class="pub">owner</span> = owner <span class="pub"># public</span>
<span class="self">self</span>.<span class="prot">_pin</span> = "1234" <span class="prot"># protected (convention)</span>
<span class="self">self</span>.<span class="priv">__balance</span> = balance <span class="priv"># private (name-mangled)</span>
<span class="kw">def</span> get_balance(<span class="self">self</span>):
<span class="kw">return</span> <span class="self">self</span>.<span class="priv">__balance</span>
<span class="kw">def</span> withdraw(<span class="self">self</span>, amount):
<span class="kw">if</span> amount > <span class="self">self</span>.<span class="priv">__balance</span>:
<span class="kw">return</span> "Insufficient funds"
<span class="self">self</span>.<span class="priv">__balance</span> -= amount</span></div>
<div class="legend-row">
<span class="legend-item"><span class="legend-dot" style="background:var(--pub)"></span>public — open to anyone</span>
<span class="legend-item"><span class="legend-dot" style="background:var(--prot)"></span>_protected — accessible, "please don't"</span>
<span class="legend-item"><span class="legend-dot" style="background:var(--priv)"></span>__private — name-mangled, hard to reach by accident</span>
</div>
<div class="setup-row">
<input id="ownerInput" type="text" value="Alice" placeholder="owner" />
<input id="balanceInput" type="text" value="100" placeholder="balance" />
<button class="setup-btn" id="createBtn">Create: BankAccount("Alice", 100)</button>
</div>
<div class="try-grid" id="tryGrid"></div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Terminal</div>
<div class="device-body" id="enLog"><div class="log-empty">Create an account, then try accessing it different ways.</div></div>
</div>
<div class="reset-row"><button class="reset-btn" id="resetBtn">↺ Reset</button></div>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pyencap')
|| document.querySelector('.infographic-pyencap');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
var ownerInput = root.querySelector('#ownerInput');
var balanceInput = root.querySelector('#balanceInput');
var createBtn = root.querySelector('#createBtn');
var tryGrid = root.querySelector('#tryGrid');
var enLog = root.querySelector('#enLog');
var resetBtn = root.querySelector('#resetBtn');
var firstLog = true;
var account = null; // { owner, pin, balance }
function updateCreateBtn() {
var o = (ownerInput.value || '').trim() || 'Alice';
var b = (balanceInput.value || '').trim() || '100';
createBtn.textContent = 'Create: BankAccount("' + o + '", ' + b + ')';
}
ownerInput.addEventListener('input', updateCreateBtn);
balanceInput.addEventListener('input', updateCreateBtn);
function logLine(text, cls) {
if (firstLog) { enLog.innerHTML = ''; firstLog = false; }
var line = document.createElement('div');
line.className = 'log-line' + (cls ? ' ' + cls : '');
line.innerHTML = '<span class="p">>>></span><span class="out">' + text + '</span>';
enLog.appendChild(line);
enLog.scrollTop = enLog.scrollHeight;
}
createBtn.addEventListener('click', function () {
var o = (ownerInput.value || '').trim() || 'Alice';
var b = parseFloat(balanceInput.value);
if (isNaN(b)) b = 100;
account = { owner: o, pin: '1234', balance: b };
logLine('account = BankAccount("' + o + '", ' + b + ')', 'ok');
});
var tries = [
{ key: 'owner', label: 'account.owner', cls: 'pub-b' },
{ key: 'pin', label: 'account._pin', cls: 'prot-b' },
{ key: 'privDirect', label: 'account.__balance', cls: 'priv-b' },
{ key: 'privMangled', label: 'account._BankAccount__balance', cls: 'priv-b' },
{ key: 'getBalance', label: 'account.get_balance()', cls: 'pub-b' },
{ key: 'withdrawTooMuch', label: 'account.withdraw(999999)', cls: 'pub-b' }
];
tries.forEach(function (t) {
var btn = document.createElement('button');
btn.className = 'try-btn ' + t.cls;
btn.textContent = t.label;
btn.addEventListener('click', function () { runTry(t.key, t.label); });
tryGrid.appendChild(btn);
});
function runTry(key, label) {
if (!account) {
logLine('NameError: name \'account\' is not defined — create one first', 'err');
return;
}
logLine(label);
if (key === 'owner') {
logLine('→ "' + account.owner + '"', 'ok');
} else if (key === 'pin') {
logLine('→ "' + account.pin + '" (works, but the _ says: internal use only)', 'warn');
} else if (key === 'privDirect') {
logLine('→ AttributeError: \'BankAccount\' object has no attribute \'__balance\'', 'err');
} else if (key === 'privMangled') {
logLine('→ ' + account.balance + ' (Python renamed __balance to this — not truly hidden, just awkward to type)', 'warn');
} else if (key === 'getBalance') {
logLine('→ ' + account.balance + ' (the intended, safe way in)', 'ok');
} else if (key === 'withdrawTooMuch') {
logLine('→ "Insufficient funds" (the method guards the balance — direct edits couldn\'t do that)', 'ok');
}
}
resetBtn.addEventListener('click', function () {
account = null;
firstLog = true;
enLog.innerHTML = '<div class="log-empty">Create an account, then try accessing it different ways.</div>';
});
updateCreateBtn();
})();
</script>
</div>✅ Use getters/setters to access or modify private values safely.
🎭 Polymorphism
Allows same method name to behave differently for different classes.
Enables flexible and generic code.
<div class="infographic-pypoly">
<style>
.infographic-pypoly {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--c-dog: #2563eb; --c-dog-soft: #e3ebfd;
--c-cat: #9333ea; --c-cat-soft: #f3e8fd;
--c-robot: #b45309; --c-robot-soft: #fdf0dc;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
box-sizing: border-box;
max-width: 720px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pypoly[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--c-dog: #5b8def; --c-dog-soft: #202942;
--c-cat: #c58af2; --c-cat-soft: #2e2140;
--c-robot: #e0a458; --c-robot-soft: #3a2c14;
}
.infographic-pypoly *, .infographic-pypoly *::before, .infographic-pypoly *::after { box-sizing: border-box; }
.infographic-pypoly .py-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pypoly h1 {
font-family: var(--display-font); font-weight: 600;
font-size: clamp(1.5rem, 5vw, 2.05rem); text-align: center; margin: 1.5rem 0 0.25rem; letter-spacing: -0.01em;
}
.infographic-pypoly .py-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.3rem; }
.infographic-pypoly .code-block {
font-family: var(--mono-font); font-size: 0.78rem; line-height: 1.7; white-space: pre;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 8px;
padding: 0.8rem 1rem; margin-bottom: 1.2rem;
}
.infographic-pypoly .code-block .kw { color: var(--accent); font-weight: 700; }
.infographic-pypoly .code-block .loop-line { border-radius: 4px; transition: background 0.25s, box-shadow 0.25s; }
.infographic-pypoly .code-block .loop-line.is-active { background: var(--accent-soft); box-shadow: 0 0 0 1.5px var(--accent); }
.infographic-pypoly .add-row { display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 0.7rem; flex-wrap: wrap; }
.infographic-pypoly .add-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700;
background: var(--paper-alt); border: 1.5px solid var(--line); color: var(--ink);
padding: 0.4rem 0.75rem; cursor: pointer; border-radius: 999px;
}
.infographic-pypoly .add-btn.dog-b { border-color: var(--c-dog); color: var(--c-dog); }
.infographic-pypoly .add-btn.cat-b { border-color: var(--c-cat); color: var(--c-cat); }
.infographic-pypoly .add-btn.robot-b { border-color: var(--c-robot); color: var(--c-robot); }
.infographic-pypoly .zoo-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.4rem; margin-bottom: 0.7rem; min-height: 44px; align-items: center; }
.infographic-pypoly .zoo-chip {
font-family: var(--mono-font); font-size: 0.78rem; font-weight: 700; padding: 0.35rem 0.65rem;
border-radius: 999px; border: 1.5px solid var(--line); background: var(--paper-alt); color: var(--ink);
transition: box-shadow 0.2s, transform 0.2s; animation: py-pop 0.25s ease;
}
@keyframes py-pop { from { opacity: 0; transform: scale(0.7); } to { opacity: 1; transform: scale(1); } }
.infographic-pypoly .zoo-chip.dog-c { border-color: var(--c-dog); color: var(--c-dog); }
.infographic-pypoly .zoo-chip.cat-c { border-color: var(--c-cat); color: var(--c-cat); }
.infographic-pypoly .zoo-chip.robot-c { border-color: var(--c-robot); color: var(--c-robot); }
.infographic-pypoly .zoo-chip.is-active { transform: scale(1.12); box-shadow: 0 0 0 3px currentColor; }
.infographic-pypoly .zoo-empty { font-family: var(--mono-font); font-size: 0.8rem; color: var(--ink-soft); }
.infographic-pypoly .run-row { display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 1rem; }
.infographic-pypoly .run-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700;
background: var(--accent); color: #fff; border: none; border-radius: 999px;
padding: 0.55rem 1.2rem; cursor: pointer;
}
.infographic-pypoly .run-btn:disabled { opacity: 0.5; cursor: default; }
.infographic-pypoly .clear-btn {
font: inherit; font-size: 0.78rem; font-weight: 600; color: var(--ink-soft);
background: none; border: 1px solid var(--line); padding: 0.45rem 0.8rem; cursor: pointer; border-radius: 999px;
}
.infographic-pypoly .device {
--ed-bg: #1e1e1e; --ed-bar: #2d2d2d; --ed-text: #d4d4d4; --ed-soft: #8a8a8a; --ed-ok: #6fdc8c;
background: var(--ed-bg); border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
}
.infographic-pypoly .device-bar {
display: flex; align-items: center; gap: 0.4rem;
background: var(--ed-bar); padding: 0.5rem 0.7rem; border-bottom: 1px solid #3a3a3a;
font-size: 0.72rem; color: var(--ed-soft); font-weight: 600;
}
.infographic-pypoly .device-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: #5a5a5a; }
.infographic-pypoly .device-bar .dot:nth-child(1) { background: #ff5f56; }
.infographic-pypoly .device-body { padding: 0.75rem 0.9rem; font-family: var(--mono-font); font-size: 0.8rem; color: var(--ed-text); min-height: 60px; max-height: 200px; overflow-y: auto; }
.infographic-pypoly .log-line { line-height: 1.7; opacity: 0; animation: py-fadein 0.2s ease forwards; }
@keyframes py-fadein { from { opacity: 0; } to { opacity: 1; } }
.infographic-pypoly .log-line.call { color: var(--ed-soft); }
.infographic-pypoly .log-line.out { color: #fff; font-weight: 600; }
.infographic-pypoly .log-empty { color: var(--ed-soft); }
.infographic-pypoly .py-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-top: 0.9rem; }
@media (prefers-reduced-motion: reduce) {
.infographic-pypoly *, .infographic-pypoly *::before, .infographic-pypoly *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="py-wrap">
<div class="code-block"><span class="kw">class</span> Dog: <span class="kw">def</span> speak(self): <span class="kw">return</span> f"{self.name} says Woof!"
<span class="kw">class</span> Cat: <span class="kw">def</span> speak(self): <span class="kw">return</span> f"{self.name} says Meow!"
<span class="kw">class</span> Robot: <span class="kw">def</span> speak(self): <span class="kw">return</span> f"{self.name} says BEEP BOOP"
<span class="loop-line" id="loopLine"><span class="kw">for</span> creature <span class="kw">in</span> zoo:
print(creature.speak())</span></div>
<div class="add-row">
<button class="add-btn dog-b" id="addDog">+ Dog</button>
<button class="add-btn cat-b" id="addCat">+ Cat</button>
<button class="add-btn robot-b" id="addRobot">+ Robot</button>
</div>
<div class="zoo-row" id="zooRow"></div>
<div class="run-row">
<button class="run-btn" id="runBtn">Run: for creature in zoo: creature.speak()</button>
<button class="clear-btn" id="clearBtn">↺ Clear zoo</button>
</div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Terminal</div>
<div class="device-body" id="pyLog"><div class="log-empty">Add a few creatures, then run the loop.</div></div>
</div>
<p class="py-caption">Every one of them gets called the exact same way — creature.speak() — but each answers as itself.</p>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pypoly')
|| document.querySelector('.infographic-pypoly');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
var speakFns = {
Dog: function (n) { return n + ' says Woof!'; },
Cat: function (n) { return n + ' says Meow!'; },
Robot: function (n) { return n + ' says BEEP BOOP'; }
};
var classPrefix = { Dog: 'dog', Cat: 'cat', Robot: 'robot' };
var zoo = []; // { type, name }
var counts = { Dog: 0, Cat: 0, Robot: 0 };
var MAX_ZOO = 6;
var zooRow = root.querySelector('#zooRow');
var runBtn = root.querySelector('#runBtn');
var clearBtn = root.querySelector('#clearBtn');
var pyLog = root.querySelector('#pyLog');
var loopLine = root.querySelector('#loopLine');
var firstLog = true;
var running = false;
function renderZoo() {
zooRow.innerHTML = '';
if (zoo.length === 0) {
var e = document.createElement('span');
e.className = 'zoo-empty';
e.textContent = 'zoo = [] — add some creatures';
zooRow.appendChild(e);
} else {
zoo.forEach(function (c, i) {
var chip = document.createElement('span');
chip.className = 'zoo-chip ' + classPrefix[c.type] + '-c';
chip.textContent = c.name;
chip.dataset.idx = i;
zooRow.appendChild(chip);
});
}
runBtn.disabled = zoo.length === 0 || running;
}
function addCreature(type) {
if (zoo.length >= MAX_ZOO) return;
counts[type]++;
zoo.push({ type: type, name: type + counts[type] });
renderZoo();
}
root.querySelector('#addDog').addEventListener('click', function () { addCreature('Dog'); });
root.querySelector('#addCat').addEventListener('click', function () { addCreature('Cat'); });
root.querySelector('#addRobot').addEventListener('click', function () { addCreature('Robot'); });
clearBtn.addEventListener('click', function () {
if (running) return;
zoo = [];
counts = { Dog: 0, Cat: 0, Robot: 0 };
firstLog = true;
pyLog.innerHTML = '<div class="log-empty">Add a few creatures, then run the loop.</div>';
renderZoo();
});
function logLine(text, cls) {
if (firstLog) { pyLog.innerHTML = ''; firstLog = false; }
var line = document.createElement('div');
line.className = 'log-line' + (cls ? ' ' + cls : '');
line.textContent = text;
pyLog.appendChild(line);
pyLog.scrollTop = pyLog.scrollHeight;
}
runBtn.addEventListener('click', function () {
if (running || zoo.length === 0) return;
running = true;
runBtn.disabled = true;
firstLog = true;
pyLog.innerHTML = '';
loopLine.classList.add('is-active');
var i = 0;
function next() {
root.querySelectorAll('.zoo-chip').forEach(function (c) { c.classList.remove('is-active'); });
if (i >= zoo.length) {
loopLine.classList.remove('is-active');
running = false;
renderZoo();
return;
}
var c = zoo[i];
var chipEl = zooRow.querySelector('[data-idx="' + i + '"]');
if (chipEl) chipEl.classList.add('is-active');
logLine('creature.speak() # ' + c.name + ' is a ' + c.type, 'call');
setTimeout(function () {
logLine('"' + speakFns[c.type](c.name) + '"', 'out');
i++;
setTimeout(next, 380);
}, 280);
}
next();
});
renderZoo();
})();
</script>
</div>✅ This is method overriding — the method sound() behaves differently for each object.
🧊 Abstraction
Focuses on what an object does, not how it does it.
Hides internal details to reduce complexity.
Implemented using abstract classes (
abcmodule in Python).
<div class="infographic-pyabstract">
<style>
.infographic-pyabstract {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--err: #c0392b; --err-soft: #fbe9e7;
--ok: #1f9d55; --ok-soft: #e3f5ec;
--mono-font: 'IBM Plex Mono', 'SFMono-Regular', Menlo, monospace;
--display-font: 'Fraunces', Georgia, serif;
--body-font: 'Inter', system-ui, sans-serif;
box-sizing: border-box;
max-width: 700px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pyabstract[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--err: #e57373; --err-soft: #3a2222;
--ok: #6fdc8c; --ok-soft: #16342a;
}
.infographic-pyabstract *, .infographic-pyabstract *::before, .infographic-pyabstract *::after { box-sizing: border-box; }
.infographic-pyabstract .ab-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pyabstract h1 {
font-family: var(--display-font); font-weight: 600;
font-size: clamp(1.5rem, 5vw, 2.05rem); text-align: center; margin: 1.5rem 0 0.25rem; letter-spacing: -0.01em;
}
.infographic-pyabstract .ab-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.3rem; }
.infographic-pyabstract .code-block {
font-family: var(--mono-font); font-size: 0.78rem; line-height: 1.7; white-space: pre;
background: var(--paper-alt); border: 1px solid var(--line); border-radius: 8px;
padding: 0.8rem 1rem; margin-bottom: 1.2rem;
}
.infographic-pyabstract .code-block .kw { color: var(--accent); font-weight: 700; }
.infographic-pyabstract .code-block .deco { color: var(--err); font-weight: 700; }
.infographic-pyabstract .code-block .cm { color: var(--ink-soft); }
.infographic-pyabstract .picker-row { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 0.7rem; flex-wrap: wrap; }
.infographic-pyabstract .picker-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.82rem; font-weight: 700;
background: var(--paper-alt); border: 1.5px solid var(--line); color: var(--ink-soft);
padding: 0.42rem 0.75rem; cursor: pointer; border-radius: 6px;
}
.infographic-pyabstract .picker-btn.is-selected { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.infographic-pyabstract .params-row { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 0.7rem; flex-wrap: wrap; min-height: 2.2em; align-items: center; }
.infographic-pyabstract .params-row input {
font-family: var(--mono-font); font-size: 0.82rem; padding: 0.4rem 0.55rem;
border: 1.5px solid var(--line); border-radius: 5px; background: var(--paper-alt); color: var(--ink); width: 80px;
text-align: center;
}
.infographic-pyabstract .params-row input:focus { outline: none; border-color: var(--accent); }
.infographic-pyabstract .params-row span { font-family: var(--mono-font); font-size: 0.78rem; color: var(--ink-soft); }
.infographic-pyabstract .create-row { display: flex; justify-content: center; margin-bottom: 1.1rem; }
.infographic-pyabstract .create-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700;
background: var(--accent); color: #fff; border: none; border-radius: 999px;
padding: 0.55rem 1.2rem; cursor: pointer;
}
.infographic-pyabstract .obj-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.8rem; margin-bottom: 1rem; }
.infographic-pyabstract .obj-card {
border: 2px solid var(--ok); background: var(--ok-soft); border-radius: 10px;
padding: 0.8rem 0.9rem; min-width: 170px; animation: ab-pop-in 0.3s ease; text-align: center;
}
@keyframes ab-pop-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
.infographic-pyabstract .obj-title { font-family: var(--mono-font); font-size: 0.78rem; font-weight: 700; color: var(--ok); margin-bottom: 0.5rem; }
.infographic-pyabstract .obj-call-btn {
display: block; width: 100%; margin-top: 0.3rem;
font: inherit; font-family: var(--mono-font); font-size: 0.74rem; font-weight: 700;
background: var(--paper); color: var(--ok); border: 1.5px solid var(--ok); border-radius: 6px;
padding: 0.32rem 0.5rem; cursor: pointer;
}
.infographic-pyabstract .obj-result { font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700; color: var(--ink); margin-top: 0.4rem; min-height: 1.3em; }
.infographic-pyabstract .device {
--ed-bg: #1e1e1e; --ed-bar: #2d2d2d; --ed-text: #d4d4d4; --ed-soft: #8a8a8a; --ed-err: #ff6b6b;
background: var(--ed-bg); border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
}
.infographic-pyabstract .device-bar {
display: flex; align-items: center; gap: 0.4rem;
background: var(--ed-bar); padding: 0.5rem 0.7rem; border-bottom: 1px solid #3a3a3a;
font-size: 0.72rem; color: var(--ed-soft); font-weight: 600;
}
.infographic-pyabstract .device-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: #5a5a5a; }
.infographic-pyabstract .device-bar .dot:nth-child(1) { background: #ff5f56; }
.infographic-pyabstract .device-body { padding: 0.75rem 0.9rem; font-family: var(--mono-font); font-size: 0.8rem; color: var(--ed-text); min-height: 50px; max-height: 170px; overflow-y: auto; }
.infographic-pyabstract .log-line { display: flex; gap: 0.5rem; line-height: 1.7; opacity: 0; animation: ab-fadein 0.25s ease forwards; flex-wrap: wrap; }
@keyframes ab-fadein { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: translateY(0); } }
.infographic-pyabstract .log-line .out { color: #fff; }
.infographic-pyabstract .log-line.err .out { color: var(--ed-err); font-weight: 700; }
.infographic-pyabstract .log-empty { color: var(--ed-soft); }
.infographic-pyabstract .ab-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-top: 0.9rem; }
.infographic-pyabstract .ab-caption strong { color: var(--ink); font-family: var(--mono-font); }
@media (prefers-reduced-motion: reduce) {
.infographic-pyabstract *, .infographic-pyabstract *::before, .infographic-pyabstract *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="ab-wrap">
<div class="code-block"><span class="kw">from</span> abc <span class="kw">import</span> ABC, abstractmethod
<span class="kw">class</span> Shape(ABC):
<span class="deco">@abstractmethod</span>
<span class="kw">def</span> area(self): <span class="cm"># no body — just a promise</span>
pass
<span class="kw">class</span> Circle(Shape):
<span class="kw">def</span> area(self): <span class="kw">return</span> 3.14159 * self.radius ** 2
<span class="kw">class</span> Rectangle(Shape):
<span class="kw">def</span> area(self): <span class="kw">return</span> self.width * self.height</div>
<div class="picker-row">
<button class="picker-btn is-selected" data-cls="Shape">Shape</button>
<button class="picker-btn" data-cls="Circle">Circle</button>
<button class="picker-btn" data-cls="Rectangle">Rectangle</button>
</div>
<div class="params-row" id="paramsRow"></div>
<div class="create-row"><button class="create-btn" id="createBtn">Try: Shape()</button></div>
<div class="obj-grid" id="objGrid"></div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Terminal</div>
<div class="device-body" id="abLog"><div class="log-empty">Try creating a Shape directly, then try a real one.</div></div>
</div>
<p class="ab-caption" id="abCaption">Shape only promises that area() will exist — it never says how. That's abstraction.</p>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pyabstract')
|| document.querySelector('.infographic-pyabstract');
var bodyAttr = document.body.getAttribute('data-darkmode');
var dark;
if (bodyAttr === 'true') { dark = true; }
else if (bodyAttr === 'false') { dark = false; }
else {
var match = document.cookie.match(/(?:^|;\s*)darkMode=([^;]+)/);
dark = match ? match[1] === 'true' : false;
}
if (dark) root.setAttribute('data-theme', 'dark');
var pickerBtns = root.querySelectorAll('.picker-btn');
var paramsRow = root.querySelector('#paramsRow');
var createBtn = root.querySelector('#createBtn');
var objGrid = root.querySelector('#objGrid');
var abLog = root.querySelector('#abLog');
var abCaption = root.querySelector('#abCaption');
var firstLog = true;
var currentCls = 'Shape';
var objCount = 0;
var captions = {
Shape: 'Shape only promises that area() will exist — it never says how. That\'s abstraction.',
Circle: 'You call .area() and get a number — you never need to see the 3.14159 * r ** 2 underneath.',
Rectangle: 'Same interface as Circle: call .area(), get a number. The formula is somebody else\'s problem.'
};
function renderParams() {
paramsRow.innerHTML = '';
if (currentCls === 'Circle') {
paramsRow.innerHTML = '<span>radius =</span><input id="p1" type="text" value="5" />';
createBtn.textContent = 'Try: Circle(radius=5)';
} else if (currentCls === 'Rectangle') {
paramsRow.innerHTML = '<span>width =</span><input id="p1" type="text" value="4" /><span>height =</span><input id="p2" type="text" value="6" />';
createBtn.textContent = 'Try: Rectangle(width=4, height=6)';
} else {
paramsRow.innerHTML = '<span style="color:var(--ink-soft);">Shape has no parameters — it can\'t be built at all.</span>';
createBtn.textContent = 'Try: Shape()';
}
var p1 = root.querySelector('#p1');
var p2 = root.querySelector('#p2');
if (p1) p1.addEventListener('input', updateCreateBtn);
if (p2) p2.addEventListener('input', updateCreateBtn);
updateCreateBtn();
}
function updateCreateBtn() {
if (currentCls === 'Circle') {
var r = root.querySelector('#p1');
createBtn.textContent = 'Try: Circle(radius=' + (r ? r.value || 0 : 5) + ')';
} else if (currentCls === 'Rectangle') {
var w = root.querySelector('#p1'), h = root.querySelector('#p2');
createBtn.textContent = 'Try: Rectangle(width=' + (w ? w.value || 0 : 4) + ', height=' + (h ? h.value || 0 : 6) + ')';
}
}
pickerBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
pickerBtns.forEach(function (b) { b.classList.toggle('is-selected', b === btn); });
currentCls = btn.dataset.cls;
abCaption.textContent = captions[currentCls];
renderParams();
});
});
function logLine(text, cls) {
if (firstLog) { abLog.innerHTML = ''; firstLog = false; }
var line = document.createElement('div');
line.className = 'log-line' + (cls ? ' ' + cls : '');
line.innerHTML = '<span class="out">' + text + '</span>';
abLog.appendChild(line);
abLog.scrollTop = abLog.scrollHeight;
}
createBtn.addEventListener('click', function () {
if (currentCls === 'Shape') {
logLine('shape = Shape()');
logLine('TypeError: Can\'t instantiate abstract class Shape with abstract method area', 'err');
return;
}
objCount++;
var varName = 'obj' + objCount;
var area, label;
if (currentCls === 'Circle') {
var r = parseFloat(root.querySelector('#p1').value) || 0;
area = Math.round(3.14159 * r * r * 100) / 100;
label = varName + ' = Circle(radius=' + r + ')';
} else {
var w = parseFloat(root.querySelector('#p1').value) || 0;
var h = parseFloat(root.querySelector('#p2').value) || 0;
area = w * h;
label = varName + ' = Rectangle(width=' + w + ', height=' + h + ')';
}
logLine(label);
var card = document.createElement('div');
card.className = 'obj-card';
card.innerHTML =
'<div class="obj-title">' + label + '</div>' +
'<button class="obj-call-btn">call ' + varName + '.area()</button>' +
'<div class="obj-result"></div>';
objGrid.appendChild(card);
var btnEl = card.querySelector('.obj-call-btn');
var resultEl = card.querySelector('.obj-result');
btnEl.addEventListener('click', function () {
resultEl.textContent = area;
});
});
renderParams();
})();
</script>
</div>✅ Abstract classes cannot be instantiated; only their subclasses can be.
🧬 Inheritance (Recap)
Allows one class to inherit attributes and methods from another.
Enables code reuse and a hierarchical relationship.
class Animal:
def speak(self):
print("Some sound")
class Dog(Animal):
def bark(self):
print("Woof")🧠 Visual Summary:
🧱 Pillar | 🚀 Concept | 🧩 Purpose |
|---|---|---|
🔐 Encapsulation | Restricts access using private variables | Security + controlled access |
🧬 Inheritance | One class inherits from another | Code reuse + hierarchy |
🎭 Polymorphism | Same method name, different behavior | Flexibility + dynamic behavior |
🧊 Abstraction | Hides details, shows only essentials | Simplifies complex systems |