π Virtual Environments
A virtual environment is an isolated Python environment created for a project.
It allows each project to have its own packages and package versions without affecting other projects.
π€ Why Use Virtual Environments?
Suppose you have two projects:
Project A β Django 4
Project B β Django 5Installing both globally can cause version conflicts.
With virtual environments:
π Project A
βββ π Virtual Environment β Django 4
π Project B
βββ π Virtual Environment β Django 5Each project stays independent.
π οΈ Create a Virtual Environment
python -m venv venvHere:
python -m venvβ Creates a virtual environmentvenvβ Name of the environment
βΆοΈ Activate It
Windows
venv\Scripts\activatemacOS / Linux
source venv/bin/activateOnce activated, packages installed using pip are installed inside that environment.
pip install requestsβ Deactivate
When you're finished:
deactivateVirtual Environment = A separate Python workspace for each project.
<div class="infographic-pyvenv">
<style>
.infographic-pyvenv {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--ok: #1f9d55; --ok-soft: #e3f5ec;
--err: #c0392b; --err-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: 760px;
margin: 0 auto;
font-family: var(--body-font);
color: var(--ink);
background: var(--paper);
}
.infographic-pyvenv[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
--ok: #6fdc8c; --ok-soft: #16342a;
--err: #e57373; --err-soft: #3a2222;
}
.infographic-pyvenv *, .infographic-pyvenv *::before, .infographic-pyvenv *::after { box-sizing: border-box; }
.infographic-pyvenv .vv-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pyvenv 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-pyvenv .vv-sub { text-align: center; color: var(--ink-soft); font-size: 0.86rem; margin: 0 0 1.4rem; }
.infographic-pyvenv .toggle-row { display: flex; justify-content: center; gap: 0.4rem; margin-bottom: 1.1rem; }
.infographic-pyvenv .toggle-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.45rem 0.9rem; cursor: pointer; border-radius: 6px;
}
.infographic-pyvenv .toggle-btn.is-selected { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.infographic-pyvenv .scene { min-height: 190px; margin-bottom: 0.6rem; }
.infographic-pyvenv .conflict-box {
border: 2px solid var(--err); background: var(--err-soft); border-radius: 10px;
padding: 1rem; text-align: center; max-width: 380px; margin: 0 auto;
}
.infographic-pyvenv .conflict-title { font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; color: var(--err); margin-bottom: 0.7rem; }
.infographic-pyvenv .pkg-line {
font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; color: var(--ink);
background: var(--paper); border: 1px solid var(--line); border-radius: 6px; padding: 0.4rem 0.6rem; margin-bottom: 0.4rem;
}
.infographic-pyvenv .conflict-warn { font-size: 0.78rem; color: var(--err); font-weight: 700; margin-top: 0.5rem; }
.infographic-pyvenv .isolated-grid { display: flex; justify-content: center; gap: 0.8rem; flex-wrap: wrap; }
.infographic-pyvenv .isolated-box {
border: 2px solid var(--ok); background: var(--ok-soft); border-radius: 10px; padding: 0.9rem 1rem;
text-align: center; min-width: 180px;
}
.infographic-pyvenv .isolated-title { font-family: var(--mono-font); font-size: 0.78rem; font-weight: 700; color: var(--ok); margin-bottom: 0.6rem; }
.infographic-pyvenv .isolated-pkg { font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; color: var(--ink); }
.infographic-pyvenv .isolated-ok { font-size: 0.72rem; color: var(--ok); font-weight: 700; margin-top: 0.5rem; }
.infographic-pyvenv .vv-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); margin-bottom: 1.6rem; }
.infographic-pyvenv .section-title {
text-align: center; font-family: var(--mono-font); font-size: 0.78rem; font-weight: 700;
text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); margin-bottom: 0.9rem;
}
.infographic-pyvenv .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; margin-bottom: 0.8rem;
}
.infographic-pyvenv .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-pyvenv .device-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: #5a5a5a; }
.infographic-pyvenv .device-bar .dot:nth-child(1) { background: #ff5f56; }
.infographic-pyvenv .device-body { padding: 0.8rem 0.9rem; font-family: var(--mono-font); font-size: 0.85rem; color: var(--ed-text); min-height: 3.5em; }
.infographic-pyvenv .prompt-line .p { color: var(--ed-ok); font-weight: 700; }
.infographic-pyvenv .prompt-line .env-tag { color: #e0a458; font-weight: 700; }
.infographic-pyvenv .controls { display: flex; align-items: center; justify-content: center; gap: 0.8rem; margin-bottom: 0.5rem; }
.infographic-pyvenv .ctrl-btn {
font: inherit; font-size: 0.82rem; font-weight: 700;
border: 1px solid var(--accent); background: var(--accent); color: #fff;
padding: 0.45rem 1rem; cursor: pointer; border-radius: 4px;
}
.infographic-pyvenv .ctrl-btn.secondary { background: var(--paper); color: var(--accent); }
.infographic-pyvenv .ctrl-btn:disabled { opacity: 0.4; cursor: default; border-color: var(--line); background: var(--paper-alt); color: var(--ink-soft); }
.infographic-pyvenv .step-progress { text-align: center; font-size: 0.72rem; color: var(--ink-soft); margin-bottom: 0.6rem; }
.infographic-pyvenv .step-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); min-height: 1.3em; }
.infographic-pyvenv .step-caption strong { color: var(--ink); font-family: var(--mono-font); }
@media (prefers-reduced-motion: reduce) {
.infographic-pyvenv *, .infographic-pyvenv *::before, .infographic-pyvenv *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="vv-wrap">
<h1>Virtual Environments</h1>
<p class="vv-sub">A private set of packages for each project, so they never step on each other.</p>
<div class="toggle-row">
<button class="toggle-btn is-selected" data-mode="without">Without a venv</button>
<button class="toggle-btn" data-mode="with">With a venv</button>
</div>
<div class="scene" id="scene"></div>
<p class="vv-caption" id="sceneCaption">Both projects install into the same global Python β only one version of Flask can exist there at a time.</p>
<div class="section-title">Create, activate, use, deactivate</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="venvTerminal"></div>
</div>
<div class="controls">
<button class="ctrl-btn secondary" id="backBtn">Back</button>
<button class="ctrl-btn" id="nextBtn">Next β</button>
<button class="ctrl-btn secondary" id="restartBtn">Restart</button>
</div>
<p class="step-progress" id="stepProgress"></p>
<p class="step-caption" id="stepCaption"></p>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pyvenv')
|| document.querySelector('.infographic-pyvenv');
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');
/* ---------------- comparison scene ---------------- */
var toggleBtns = root.querySelectorAll('.toggle-btn');
var scene = root.querySelector('#scene');
var sceneCaption = root.querySelector('#sceneCaption');
var scenes = {
without: {
html:
'<div class="conflict-box">' +
'<div class="conflict-title">global site-packages</div>' +
'<div class="pkg-line">Flask 2.0 β needed by Project A</div>' +
'<div class="pkg-line">Flask 3.0 β needed by Project B</div>' +
'<div class="conflict-warn">β only one version can live here at a time</div>' +
'</div>',
caption: 'Both projects install into the same global Python β only one version of Flask can exist there at a time.'
},
with: {
html:
'<div class="isolated-grid">' +
'<div class="isolated-box"><div class="isolated-title">Project A / venv</div><div class="isolated-pkg">Flask 2.0</div><div class="isolated-ok">β isolated</div></div>' +
'<div class="isolated-box"><div class="isolated-title">Project B / venv</div><div class="isolated-pkg">Flask 3.0</div><div class="isolated-ok">β isolated</div></div>' +
'</div>',
caption: 'Each project gets its own private folder of packages β both versions coexist without conflict.'
}
};
toggleBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
toggleBtns.forEach(function (b) { b.classList.toggle('is-selected', b === btn); });
var s = scenes[btn.dataset.mode];
scene.innerHTML = s.html;
sceneCaption.textContent = s.caption;
});
});
scene.innerHTML = scenes.without.html;
/* ---------------- terminal workflow stepper ---------------- */
var venvTerminal = root.querySelector('#venvTerminal');
var backBtn = root.querySelector('#backBtn');
var nextBtn = root.querySelector('#nextBtn');
var restartBtn = root.querySelector('#restartBtn');
var stepProgress = root.querySelector('#stepProgress');
var stepCaption = root.querySelector('#stepCaption');
var steps = [
{ prompt: '$', line: 'python -m venv env', caption: 'Creates a new folder called env β a self-contained copy of Python and its own site-packages.' },
{ prompt: '$', line: 'source env/bin/activate', caption: 'Activates it. Your shell prompt changes to show you\'re inside env now.' },
{ prompt: '(env) $', line: 'pip install flask', caption: 'Anything you install now goes into env only β never the global Python.', envTag: true },
{ prompt: '(env) $', line: 'python app.py', caption: 'Your app runs using exactly the packages inside env.', envTag: true },
{ prompt: '(env) $', line: 'deactivate', caption: 'Leaves the virtual environment. The prompt returns to normal.', envTag: true },
{ prompt: '$', line: '# back to normal β env is untouched, ready for next time', caption: 'Back to the global Python β env and its packages are untouched, waiting for next time.' }
];
var idx = -1;
function renderTerminal() {
venvTerminal.innerHTML = '';
for (var i = 0; i <= idx; i++) {
var s = steps[i];
var line = document.createElement('div');
line.className = 'prompt-line';
var promptHtml = s.envTag || s.prompt.indexOf('(env)') !== -1
? '<span class="env-tag">' + s.prompt + '</span>'
: '<span class="p">' + s.prompt + '</span>';
line.innerHTML = promptHtml + ' ' + s.line;
venvTerminal.appendChild(line);
}
if (idx === -1) {
var placeholder = document.createElement('div');
placeholder.style.color = 'var(--ed-soft)';
placeholder.textContent = 'Press Next to start.';
venvTerminal.appendChild(placeholder);
}
stepProgress.textContent = 'Step ' + (idx + 2) + ' of ' + (steps.length + 1);
backBtn.disabled = idx < 0;
nextBtn.disabled = idx >= steps.length - 1;
nextBtn.textContent = idx >= steps.length - 1 ? 'Done' : 'Next β';
stepCaption.innerHTML = idx >= 0 ? steps[idx].caption : 'Each step below is a real command you\'d type.';
}
nextBtn.addEventListener('click', function () {
if (idx < steps.length - 1) { idx++; renderTerminal(); }
});
backBtn.addEventListener('click', function () {
if (idx > -1) { idx--; renderTerminal(); }
});
restartBtn.addEventListener('click', function () {
idx = -1;
renderTerminal();
});
renderTerminal();
})();
</script>
</div>