π Mutable and Immutable Objects
In Python, objects are either mutable or immutable depending on whether their contents can be changed after they are created.
π’ Mutable Objects
Mutable objects can be changed after creation.
Common mutable types:
list
set
dictionaryExample:
numbers = [1, 2, 3]
numbers[0] = 10
print(numbers) # [10, 2, 3]The same list was changed.
π΄ Immutable Objects
Immutable objects cannot be changed after creation.
Common immutable types:
int
float
bool
str
tupleExample:
x = 10
x = 20Here, the original 10 is not changed. Instead, x is made to refer to a new integer object containing 20.
Strings work similarly:
name = "Python"
name[0] = "J" # β ErrorA string cannot be modified directly.
βοΈ Mutable vs Immutable
Mutable π’ | Immutable π΄ |
|---|---|
Can be changed after creation | Cannot be changed after creation |
|
|
|
|
|
|
| |
|
<div class="infographic-pymutable">
<style>
.infographic-pymutable {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--mut: #1f9d55; --mut-soft: #e3f5ec;
--immut: #b45309; --immut-soft: #fdf0dc;
--true-c: #1f9d55; --true-soft: #e3f5ec;
--false-c: #c0392b; --false-soft: #fbe9e7;
--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: 780px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pymutable[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--mut: #6fdc8c; --mut-soft: #16342a;
--immut: #e0a458; --immut-soft: #3a2c14;
--true-c: #6fdc8c; --true-soft: #1c3324;
--false-c: #e57373; --false-soft: #3a2222;
}
.infographic-pymutable *, .infographic-pymutable *::before, .infographic-pymutable *::after { box-sizing: border-box; }
.infographic-pymutable .pm-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pymutable 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-pymutable .pm-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.4rem; }
.infographic-pymutable .type-groups { display: flex; justify-content: center; gap: 1.4rem; margin-bottom: 1.3rem; flex-wrap: wrap; }
.infographic-pymutable .type-group { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.infographic-pymutable .type-group-label { font-family: var(--mono-font); font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.infographic-pymutable .type-group-label.mut-c { color: var(--mut); }
.infographic-pymutable .type-group-label.immut-c { color: var(--immut); }
.infographic-pymutable .type-row { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-content: center; }
.infographic-pymutable .type-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-soft);
padding: 0.4rem 0.7rem; cursor: pointer; border-radius: 6px;
}
.infographic-pymutable .type-btn.is-selected.mut-c { border-color: var(--mut); color: var(--mut); background: var(--mut-soft); }
.infographic-pymutable .type-btn.is-selected.immut-c { border-color: var(--immut); color: var(--immut); background: var(--immut-soft); }
.infographic-pymutable .decl-line {
text-align: center; font-family: var(--mono-font); font-size: 0.88rem; color: var(--ink-soft); margin-bottom: 1.1rem;
}
.infographic-pymutable .decl-line strong { color: var(--ink); }
.infographic-pymutable .diagram { display: flex; justify-content: center; align-items: flex-start; gap: 2.4rem; min-height: 140px; margin-bottom: 1rem; flex-wrap: wrap; }
.infographic-pymutable .var-col { display: flex; flex-direction: column; align-items: center; }
.infographic-pymutable .var-label {
font-family: var(--display-font); font-size: 1.05rem; font-weight: 600; color: var(--ink);
width: 32px; height: 32px; border: 1.5px solid var(--ink-soft); border-radius: 50%;
display: flex; align-items: center; justify-content: center; margin-bottom: 0.3rem;
}
.infographic-pymutable .var-arrow { color: var(--ink-soft); font-size: 1.1rem; margin-bottom: 0.3rem; }
.infographic-pymutable .var-box {
font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700; text-align: center;
padding: 0.6rem 0.9rem; border-radius: 8px; border: 2.5px solid var(--accent); background: var(--accent-soft); color: var(--ink);
transition: border-color 0.3s, background 0.3s, transform 0.25s;
min-width: 90px;
}
.infographic-pymutable .var-box.is-fresh { animation: pm-pop 0.35s ease; }
@keyframes pm-pop { 0% { transform: scale(0.8); opacity: 0.3; } 100% { transform: scale(1); opacity: 1; } }
.infographic-pymutable .var-addr { font-family: var(--mono-font); font-size: 0.65rem; color: var(--ink-soft); margin-top: 0.25rem; }
.infographic-pymutable .action-row { display: flex; justify-content: center; margin-bottom: 1rem; }
.infographic-pymutable .action-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-pymutable .action-btn:disabled { opacity: 0.5; cursor: default; }
.infographic-pymutable .status-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 0.9rem; }
@media (max-width: 480px) { .infographic-pymutable .status-grid { grid-template-columns: 1fr; } }
.infographic-pymutable .status-card { background: var(--paper-alt); border: 1px solid var(--line); border-radius: 6px; padding: 0.5rem 0.7rem; text-align: center; font-family: var(--mono-font); font-size: 0.8rem; }
.infographic-pymutable .status-card .badge { font-weight: 700; padding: 0.05rem 0.4rem; border-radius: 999px; margin-left: 0.3rem; }
.infographic-pymutable .status-card .badge.t { background: var(--true-soft); color: var(--true-c); }
.infographic-pymutable .status-card .badge.f { background: var(--false-soft); color: var(--false-c); }
.infographic-pymutable .pm-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-bottom: 0.5rem; min-height: 2.4em; }
.infographic-pymutable .pm-caption strong { color: var(--ink); }
.infographic-pymutable .reset-row { display: flex; justify-content: center; }
.infographic-pymutable .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-pymutable *, .infographic-pymutable *::before, .infographic-pymutable *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="pm-wrap">
<h1>Mutable & Immutable Objects</h1>
<p class="pm-sub">b = a. Now change b. Does a notice?</p>
<div class="type-groups">
<div class="type-group">
<span class="type-group-label mut-c">Mutable</span>
<div class="type-row" id="mutRow"></div>
</div>
<div class="type-group">
<span class="type-group-label immut-c">Immutable</span>
<div class="type-row" id="immutRow"></div>
</div>
</div>
<p class="decl-line" id="declLine">a = <strong>[1, 2, 3]</strong> Β· b = a</p>
<div class="diagram" id="diagram"></div>
<div class="action-row">
<button class="action-btn" id="actionBtn">Run: b.append(4)</button>
</div>
<div class="status-grid">
<div class="status-card">a is b <span class="badge t" id="isBBadge">True</span></div>
<div class="status-card">id(a) == id(b) <span class="badge t" id="idBadge">True</span></div>
</div>
<p class="pm-caption" id="pmCaption">Lists are mutable β .append() changes the object in place, so a sees it too.</p>
<div class="reset-row"><button class="reset-btn" id="resetBtn">βΊ Reset</button></div>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pymutable')
|| document.querySelector('.infographic-pymutable');
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 scenarios = {
list: { mutable: true, initial: '[1, 2, 3]', action: 'b.append(4)', after: '[1, 2, 3, 4]',
caption: 'Lists are mutable β .append() changes the object in place, so a sees it too.' },
dict: { mutable: true, initial: '{"x": 1}', action: 'b["x"] = 99', after: '{"x": 99}',
caption: 'Dicts are mutable β assigning a key updates the same object a points to.' },
set: { mutable: true, initial: '{1, 2}', action: 'b.add(3)', after: '{1, 2, 3}',
caption: 'Sets are mutable β .add() changes the object in place, no new object needed.' },
int: { mutable: false, initial: '5', action: 'b += 1', after: '6',
caption: 'Integers are immutable β b += 1 makes a brand new object. a still points to the old one.' },
float: { mutable: false, initial: '3.14', action: 'b += 1.0', after: '4.14',
caption: 'Floats are immutable β b += 1.0 creates a new float object. a keeps its original value.' },
bool: { mutable: false, initial: 'True', action: 'b = not b', after: 'False',
caption: 'Booleans are immutable β not b points to the other True/False singleton. a is untouched.' },
str: { mutable: false, initial: '"hi"', action: 'b += "!"', after: '"hi!"',
caption: 'Strings are immutable β b += "!" builds a new string. a is completely untouched.' },
tuple: { mutable: false, initial: '(1, 2)', action: 'b += (3,)', after: '(1, 2, 3)',
caption: 'Tuples are immutable β b += (3,) creates a new tuple. a keeps its original value.' }
};
var ADDR_A = '0x7f3a10a0';
var ADDR_B_NEW = '0x7f3a1140';
var mutRow = root.querySelector('#mutRow');
var immutRow = root.querySelector('#immutRow');
var declLine = root.querySelector('#declLine');
var diagram = root.querySelector('#diagram');
var actionBtn = root.querySelector('#actionBtn');
var isBBadge = root.querySelector('#isBBadge');
var idBadge = root.querySelector('#idBadge');
var pmCaption = root.querySelector('#pmCaption');
var resetBtn = root.querySelector('#resetBtn');
var currentKey = 'list';
var hasRun = false;
function makeBtn(key, cls) {
var btn = document.createElement('button');
btn.className = 'type-btn' + (key === currentKey ? ' is-selected ' + cls : '');
btn.textContent = key;
btn.dataset.key = key;
btn.addEventListener('click', function () {
root.querySelectorAll('.type-btn').forEach(function (b) {
b.classList.remove('is-selected', 'mut-c', 'immut-c');
});
btn.classList.add('is-selected', cls);
currentKey = key;
hasRun = false;
render();
});
return btn;
}
['list', 'dict', 'set'].forEach(function (k) { mutRow.appendChild(makeBtn(k, 'mut-c')); });
['int', 'float', 'bool', 'str', 'tuple'].forEach(function (k) { immutRow.appendChild(makeBtn(k, 'immut-c')); });
function renderDiagram(sc) {
diagram.innerHTML = '';
if (!hasRun || sc.mutable) {
// shared box: both a and b point to the same box
var wrap = document.createElement('div');
wrap.style.display = 'flex';
wrap.style.flexDirection = 'column';
wrap.style.alignItems = 'center';
var arrowsRow = document.createElement('div');
arrowsRow.style.display = 'flex';
arrowsRow.style.gap = '1.8rem';
['a', 'b'].forEach(function (name) {
var col = document.createElement('div');
col.className = 'var-col';
col.innerHTML = '<span class="var-label">' + name + '</span><span class="var-arrow">β</span>';
arrowsRow.appendChild(col);
});
wrap.appendChild(arrowsRow);
var box = document.createElement('div');
box.className = 'var-box' + (hasRun ? ' is-fresh' : '');
box.textContent = hasRun ? sc.after : sc.initial;
wrap.appendChild(box);
var addr = document.createElement('div');
addr.className = 'var-addr';
addr.textContent = ADDR_A;
wrap.appendChild(addr);
diagram.appendChild(wrap);
} else {
// separate boxes: a keeps old box, b points to a brand-new one
var colA = document.createElement('div');
colA.className = 'var-col';
colA.innerHTML =
'<span class="var-label">a</span><span class="var-arrow">β</span>' +
'<div class="var-box">' + sc.initial + '</div>' +
'<div class="var-addr">' + ADDR_A + '</div>';
var colB = document.createElement('div');
colB.className = 'var-col';
colB.innerHTML =
'<span class="var-label">b</span><span class="var-arrow">β</span>' +
'<div class="var-box is-fresh">' + sc.after + '</div>' +
'<div class="var-addr">' + ADDR_B_NEW + '</div>';
diagram.appendChild(colA);
diagram.appendChild(colB);
}
}
function render() {
var sc = scenarios[currentKey];
declLine.innerHTML = 'a = <strong>' + sc.initial + '</strong> Β· b = a';
actionBtn.textContent = 'Run: ' + sc.action;
actionBtn.disabled = hasRun;
renderDiagram(sc);
var same = !hasRun || sc.mutable;
isBBadge.textContent = same ? 'True' : 'False';
isBBadge.className = 'badge ' + (same ? 't' : 'f');
idBadge.textContent = same ? 'True' : 'False';
idBadge.className = 'badge ' + (same ? 't' : 'f');
pmCaption.innerHTML = hasRun
? (sc.mutable
? '<strong>a is now ' + sc.after + ' too!</strong> ' + sc.caption
: '<strong>a is still ' + sc.initial + '.</strong> ' + sc.caption)
: sc.caption;
}
actionBtn.addEventListener('click', function () {
hasRun = true;
render();
});
resetBtn.addEventListener('click', function () {
hasRun = false;
render();
});
render();
})();
</script>
</div>