π‘ Shallow Copy
A shallow copy creates a new outer object, but the nested objects are still shared.
Use copy.copy():
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)
shallow[0][0] = 100
print(original) #[[100, 2], [3, 4]]Why? Both original and shallow refer to the same nested lists.
original βββ [ [1, 2], [3, 4] ]
β
shallow βββββββπ’ Deep Copy
A deep copy creates a completely independent copy, including the nested objects.
Use copy.deepcopy():
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 100
print(original) #[[1, 2], [3, 4]]The nested lists are copied as well.
original βββ [ [1, 2], [3, 4] ]
deep βββββββ [ [1, 2], [3, 4] ]They are completely independent.
βοΈ Shallow vs Deep Copy
Shallow Copy π‘ | Deep Copy π’ |
|---|---|
Creates a new outer object | Creates a completely independent copy |
Nested objects are shared | Nested objects are also copied |
|
|
Changes to nested objects can affect original | Changes do not affect original |
β Remember
Shallow Copy β New outer object + Shared inner objects
Deep Copy β New outer object + New inner objectsFor simple, non-nested objects, the difference usually doesn't matter. The distinction becomes important when working with nested mutable objects like lists and dictionaries.
<div class="infographic-pycopy">
<style>
.infographic-pycopy {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--safe: #1f9d55; --safe-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: 760px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pycopy[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--safe: #6fdc8c; --safe-soft: #16342a;
}
.infographic-pycopy *, .infographic-pycopy *::before, .infographic-pycopy *::after { box-sizing: border-box; }
.infographic-pycopy .cp-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pycopy 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-pycopy .cp-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.4rem; }
.infographic-pycopy .method-row { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 0.5rem; flex-wrap: wrap; }
.infographic-pycopy .method-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.78rem; font-weight: 700;
background: var(--paper-alt); border: 1.5px solid var(--line); color: var(--ink-soft);
padding: 0.45rem 0.75rem; cursor: pointer; border-radius: 6px;
}
.infographic-pycopy .method-btn.is-selected { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.infographic-pycopy .legend-row { display: flex; justify-content: center; gap: 0.9rem; margin-bottom: 1.2rem; flex-wrap: wrap; }
.infographic-pycopy .legend-item { display: flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; color: var(--ink-soft); }
.infographic-pycopy .legend-dot { width: 10px; height: 10px; border-radius: 3px; }
.infographic-pycopy .diagram { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin-bottom: 1.1rem; }
@media (max-width: 540px) { .infographic-pycopy .diagram { grid-template-columns: 1fr; } }
.infographic-pycopy .col-title { text-align: center; font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700; margin-bottom: 0.5rem; }
.infographic-pycopy .outer-box {
border: 2.5px solid var(--line); border-radius: 10px; padding: 0.6rem; position: relative; margin-top: 0.7rem;
transition: border-color 0.3s, background 0.3s;
}
.infographic-pycopy .outer-box.shared { border-color: var(--accent); background: var(--accent-soft); }
.infographic-pycopy .outer-box.safe { border-color: var(--safe); background: var(--safe-soft); }
.infographic-pycopy .outer-tag {
position: absolute; top: -0.65rem; left: 0.5rem; font-size: 0.6rem; font-weight: 700;
background: var(--paper); padding: 0 0.3rem;
}
.infographic-pycopy .outer-box.shared .outer-tag { color: var(--accent); }
.infographic-pycopy .outer-box.safe .outer-tag { color: var(--safe); }
.infographic-pycopy .inner-row { display: flex; gap: 0.5rem; justify-content: center; }
.infographic-pycopy .inner-box {
border: 2px solid var(--line); border-radius: 8px; padding: 0.4rem 0.5rem; text-align: center;
background: var(--paper); transition: border-color 0.3s, background 0.3s, transform 0.2s;
}
.infographic-pycopy .inner-box.shared { border-color: var(--accent); background: var(--accent-soft); }
.infographic-pycopy .inner-box.safe { border-color: var(--safe); background: var(--safe-soft); }
.infographic-pycopy .inner-box.is-flash { animation: cp-flash 0.5s ease; }
@keyframes cp-flash { 0% { transform: scale(1); } 40% { transform: scale(1.15); } 100% { transform: scale(1); } }
.infographic-pycopy .inner-val { font-family: var(--mono-font); font-size: 0.85rem; font-weight: 700; }
.infographic-pycopy .inner-tag { font-size: 0.6rem; color: var(--ink-soft); margin-top: 0.15rem; }
.infographic-pycopy .inner-box.shared .inner-tag { color: var(--accent); }
.infographic-pycopy .inner-box.safe .inner-tag { color: var(--safe); }
.infographic-pycopy .action-row { display: flex; justify-content: center; gap: 0.6rem; margin-bottom: 1rem; flex-wrap: wrap; }
.infographic-pycopy .action-btn {
font: inherit; font-family: var(--mono-font); font-size: 0.82rem; font-weight: 700;
background: var(--accent); color: #fff; border: none; border-radius: 999px;
padding: 0.5rem 1.1rem; cursor: pointer;
}
.infographic-pycopy .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;
}
.infographic-pycopy .result-line {
text-align: center; font-family: var(--mono-font); font-size: 0.95rem; font-weight: 700; margin-bottom: 0.3rem;
}
.infographic-pycopy .result-line.changed { color: var(--accent); }
.infographic-pycopy .result-line.unchanged { color: var(--safe); }
.infographic-pycopy .cp-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-bottom: 0.4rem; }
@media (prefers-reduced-motion: reduce) {
.infographic-pycopy *, .infographic-pycopy *::before, .infographic-pycopy *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="cp-wrap">
<h1>Shallow Copy vs Deep Copy</h1>
<p class="cp-sub">original = [[1, 2], [3, 4]]. Copy it, then change the inside of original.</p>
<div class="method-row">
<button class="method-btn is-selected" data-method="assign">b = original</button>
<button class="method-btn" data-method="shallow">b = copy.copy(original)</button>
<button class="method-btn" data-method="deep">b = copy.deepcopy(original)</button>
</div>
<div class="legend-row">
<span class="legend-item"><span class="legend-dot" style="background:var(--accent)"></span>same object β changes propagate</span>
<span class="legend-item"><span class="legend-dot" style="background:var(--safe)"></span>new object β fully independent</span>
</div>
<div class="diagram">
<div>
<div class="col-title">original</div>
<div class="outer-box shared" id="origOuter">
<span class="outer-tag">list</span>
<div class="inner-row">
<div class="inner-box shared" id="origInner1">
<div class="inner-val" id="origInner1Val">[1, 2]</div>
<div class="inner-tag">list</div>
</div>
<div class="inner-box shared" id="origInner2">
<div class="inner-val">[3, 4]</div>
<div class="inner-tag">list</div>
</div>
</div>
</div>
</div>
<div>
<div class="col-title">b</div>
<div class="outer-box shared" id="bOuter">
<span class="outer-tag" id="bOuterTag">same object</span>
<div class="inner-row">
<div class="inner-box shared" id="bInner1">
<div class="inner-val" id="bInner1Val">[1, 2]</div>
<div class="inner-tag" id="bInner1Tag">same object</div>
</div>
<div class="inner-box shared" id="bInner2">
<div class="inner-val">[3, 4]</div>
<div class="inner-tag" id="bInner2Tag">same object</div>
</div>
</div>
</div>
</div>
</div>
<div class="action-row">
<button class="action-btn" id="modifyBtn">Run: original[0][0] = 99</button>
<button class="reset-btn" id="resetBtn">βΊ Reset</button>
</div>
<p class="result-line" id="resultLine">b[0][0] is also 99 β they're the same object.</p>
<p class="cp-caption" id="cpCaption">Plain assignment: original and b are two names for the exact same list.</p>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pycopy')
|| document.querySelector('.infographic-pycopy');
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 methodBtns = root.querySelectorAll('.method-btn');
var bOuter = root.querySelector('#bOuter');
var bOuterTag = root.querySelector('#bOuterTag');
var bInner1 = root.querySelector('#bInner1');
var bInner1Tag = root.querySelector('#bInner1Tag');
var bInner1Val = root.querySelector('#bInner1Val');
var bInner2 = root.querySelector('#bInner2');
var bInner2Tag = root.querySelector('#bInner2Tag');
var origInner1Val = root.querySelector('#origInner1Val');
var origInner1 = root.querySelector('#origInner1');
var modifyBtn = root.querySelector('#modifyBtn');
var resetBtn = root.querySelector('#resetBtn');
var resultLine = root.querySelector('#resultLine');
var cpCaption = root.querySelector('#cpCaption');
var captions = {
assign: 'Plain assignment: original and b are two names for the exact same list.',
shallow: 'copy.copy() makes a new outer list, but the inner lists inside are still shared.',
deep: 'copy.deepcopy() copies everything, all the way down. Nothing is shared.'
};
var currentMethod = 'assign';
var modified = false;
function setBoxState(el, shared) {
el.classList.toggle('shared', shared);
el.classList.toggle('safe', !shared);
}
function render() {
var sharesOuter = currentMethod === 'assign';
var sharesInner = currentMethod === 'assign' || currentMethod === 'shallow';
setBoxState(bOuter, sharesOuter);
bOuterTag.textContent = sharesOuter ? 'same object' : 'new object';
setBoxState(bInner1, sharesInner);
bInner1Tag.textContent = sharesInner ? 'same object' : 'new object';
setBoxState(bInner2, sharesInner);
bInner2Tag.textContent = sharesInner ? 'same object' : 'new object';
var origVal = modified ? '[99, 2]' : '[1, 2]';
origInner1Val.textContent = origVal;
bInner1Val.textContent = sharesInner ? origVal : '[1, 2]';
modifyBtn.disabled = modified;
modifyBtn.style.opacity = modified ? '0.5' : '1';
if (modified) {
var changed = sharesInner;
resultLine.textContent = changed
? 'b[0][0] is also 99 β they\'re the same object.'
: 'b[0][0] is still 1 β completely unaffected.';
resultLine.className = 'result-line ' + (changed ? 'changed' : 'unchanged');
} else {
resultLine.textContent = 'Both start as [1, 2]. Tap "Run" to modify original.';
resultLine.className = 'result-line';
}
cpCaption.textContent = captions[currentMethod];
}
methodBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
methodBtns.forEach(function (b) { b.classList.toggle('is-selected', b === btn); });
currentMethod = btn.dataset.method;
modified = false;
render();
});
});
modifyBtn.addEventListener('click', function () {
if (modified) return;
modified = true;
render();
[origInner1, bInner1].forEach(function (box) {
box.classList.remove('is-flash');
void box.offsetWidth;
box.classList.add('is-flash');
});
});
resetBtn.addEventListener('click', function () {
modified = false;
render();
});
render();
})();
</script>
</div>