When Python sees an expression with multiple operators, it needs to decide:
Which operation to do first? → Precedence
In what direction to evaluate when precedence is the same? → Associativity
🔝 Operator Precedence
Precedence means which operator gets executed first when there are no parentheses.
💡 Example:
result = 2 + 3 * 4
# NOT (2 + 3) * 4 → would be 20
# BUT: 2 + (3 * 4) → 2 + 12 = 14👉 Here, * (multiplication) has higher precedence than +, so it runs first.
🔁 Associativity
Associativity tells which direction to evaluate operators of the same precedence:
Left-to-right: Most operators like
+, ,/,//,%Right-to-left: Assignment operators and exponentiation (
=,+=,*)
🔢 Operator Precedence Table
(Top = Highest priority)
Priority | Operators | Type | Associativity |
|---|---|---|---|
1️⃣ |
| Parentheses | — |
2️⃣ |
| Exponentiation | Right to Left |
3️⃣ |
| Unary plus, minus, bitwise NOT | Right to Left |
4️⃣ |
| Multiplication, Division, Modulo | Left to Right |
5️⃣ |
| Addition, Subtraction | Left to Right |
6️⃣ |
| Comparisons | Left to Right |
7️⃣ |
| Logical NOT | Right to Left |
8️⃣ |
| Logical AND | Left to Right |
9️⃣ |
| Logical OR | Left to Right |
✅ Tip: Always use parentheses () to make your expressions clear and bug-free.
<div class="infographic-pyprec">
<style>
.infographic-pyprec {
--ink: #1a1a1a;
--ink-soft: #5c5c5c;
--paper: #ffffff;
--paper-alt: #f0ece4;
--accent: #2563eb;
--accent-soft:#e3ebfd;
--line: #ddd6c9;
--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-pyprec[data-theme="dark"] {
--ink: #e8e6e1;
--ink-soft: #9a968c;
--paper: #121212;
--paper-alt: #1e1e26;
--accent: #5b8def;
--accent-soft:#202942;
--line: #2c313d;
}
.infographic-pyprec *, .infographic-pyprec *::before, .infographic-pyprec *::after { box-sizing: border-box; }
.infographic-pyprec .pp-wrap { padding: 0 clamp(1rem, 4vw, 2rem) 1.5rem; }
.infographic-pyprec h1 {
font-family: var(--display-font); font-weight: 600;
font-size: clamp(1.55rem, 5vw, 2.15rem); text-align: center; margin: 1.5rem 0 0.25rem; letter-spacing: -0.01em;
}
.infographic-pyprec .pp-sub { text-align: center; color: var(--ink-soft); font-size: 0.88rem; margin: 0 0 1.6rem; }
.infographic-pyprec .pp-grid { display: grid; grid-template-columns: 210px 1fr; gap: 1.6rem; align-items: start; }
@media (max-width: 680px) { .infographic-pyprec .pp-grid { grid-template-columns: 1fr; } }
/* precedence ladder */
.infographic-pyprec .pp-ladder-title { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); margin-bottom: 0.5rem; }
.infographic-pyprec .pp-ladder { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
.infographic-pyprec .pp-tier {
font: inherit; text-align: left; background: var(--paper-alt); border: none; border-bottom: 1px solid var(--line);
padding: 0.45rem 0.6rem; cursor: pointer; display: flex; flex-direction: column; gap: 0.1rem;
}
.infographic-pyprec .pp-tier:last-child { border-bottom: none; }
.infographic-pyprec .pp-tier .t-ops { font-family: var(--mono-font); font-size: 0.8rem; font-weight: 700; color: var(--ink); }
.infographic-pyprec .pp-tier .t-name { font-size: 0.66rem; color: var(--ink-soft); }
.infographic-pyprec .pp-tier.is-selected { background: var(--accent-soft); }
.infographic-pyprec .pp-tier.is-selected .t-ops { color: var(--accent); }
.infographic-pyprec .pp-assoc-note { font-size: 0.75rem; color: var(--ink-soft); margin-top: 0.6rem; min-height: 2.6em; }
.infographic-pyprec .pp-assoc-note strong { color: var(--accent); }
/* right column */
.infographic-pyprec .pp-ex-row { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1.1rem; }
.infographic-pyprec .pp-ex-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.4rem 0.65rem; cursor: pointer; border-radius: 5px;
}
.infographic-pyprec .pp-ex-btn.is-selected { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.infographic-pyprec .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.9rem;
}
.infographic-pyprec .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-pyprec .device-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: #5a5a5a; }
.infographic-pyprec .device-bar .dot:nth-child(1) { background: #ff5f56; }
.infographic-pyprec .device-body {
padding: 1.1rem 1rem; font-family: var(--mono-font); font-size: 1.15rem; color: var(--ed-text);
text-align: center; min-height: 3.2em; display: flex; align-items: center; justify-content: center;
}
.infographic-pyprec .device-body mark { background: var(--accent); color: #fff; border-radius: 3px; padding: 0.05em 0.25em; }
.infographic-pyprec .device-body .final-val { color: var(--ed-ok); font-weight: 700; }
.infographic-pyprec .pp-caption { text-align: center; font-size: 0.85rem; color: var(--ink-soft); min-height: 1.3em; margin-bottom: 0.9rem; }
.infographic-pyprec .pp-controls { display: flex; align-items: center; justify-content: center; gap: 0.8rem; }
.infographic-pyprec .pp-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-pyprec .pp-btn.secondary { background: var(--paper); color: var(--accent); }
.infographic-pyprec .pp-btn:disabled { opacity: 0.4; cursor: default; border-color: var(--line); background: var(--paper-alt); color: var(--ink-soft); }
.infographic-pyprec .pp-progress { text-align: center; font-size: 0.72rem; color: var(--ink-soft); margin-top: 0.5rem; }
@media (prefers-reduced-motion: reduce) {
.infographic-pyprec *, .infographic-pyprec *::before, .infographic-pyprec *::after {
animation-duration: 0.001ms !important; transition-duration: 0.001ms !important;
}
}
</style>
<div class="pp-wrap">
<h1>Precedence & Associativity</h1>
<p class="pp-sub">Tap a tier to place it. Pick an expression and step through the order.</p>
<div class="pp-grid">
<div>
<div class="pp-ladder-title">Highest → Lowest</div>
<div class="pp-ladder" id="ppLadder"></div>
<div class="pp-assoc-note" id="ppAssocNote">Tap a tier to see its associativity.</div>
</div>
<div>
<div class="pp-ex-row" id="ppExRow"></div>
<div class="device">
<div class="device-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span> Evaluation</div>
<div class="device-body" id="ppExprLine"></div>
</div>
<p class="pp-caption" id="ppCaption"></p>
<div class="pp-controls">
<button class="pp-btn secondary" id="ppBack">Back</button>
<button class="pp-btn" id="ppNext">Next →</button>
<button class="pp-btn secondary" id="ppRestart">Restart</button>
</div>
<p class="pp-progress" id="ppProgress"></p>
</div>
</div>
</div>
<script>
(function () {
var root = document.currentScript.closest('.infographic-pyprec')
|| document.querySelector('.infographic-pyprec');
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');
/* ---------------- precedence ladder ---------------- */
var tiers = [
{ ops: ['(', ')'], name: 'Parentheses', assoc: 'evaluated first, always' },
{ ops: ['**'], name: 'Exponent', assoc: 'right-to-left' },
{ ops: ['~', 'unary +', 'unary -'], name: 'Unary', assoc: 'right-to-left' },
{ ops: ['*', '/', '//', '%'], name: 'Multiplicative', assoc: 'left-to-right' },
{ ops: ['+', '-'], name: 'Additive', assoc: 'left-to-right' },
{ ops: ['<<', '>>'], name: 'Bit shifts', assoc: 'left-to-right' },
{ ops: ['&'], name: 'Bitwise AND', assoc: 'left-to-right' },
{ ops: ['^'], name: 'Bitwise XOR', assoc: 'left-to-right' },
{ ops: ['|'], name: 'Bitwise OR', assoc: 'left-to-right' },
{ ops: ['==', '!=', '>', '<', '>=', '<=', 'is', 'in'], name: 'Comparisons', assoc: 'chained, left-to-right' },
{ ops: ['not'], name: 'Logical NOT', assoc: 'right-to-left' },
{ ops: ['and'], name: 'Logical AND', assoc: 'left-to-right' },
{ ops: ['or'], name: 'Logical OR', assoc: 'left-to-right' }
];
var ladder = root.querySelector('#ppLadder');
var assocNote = root.querySelector('#ppAssocNote');
var selectedTierIdx = -1;
tiers.forEach(function (tier, idx) {
var btn = document.createElement('button');
btn.className = 'pp-tier';
btn.innerHTML = '<span class="t-ops">' + tier.ops.join(' ') + '</span><span class="t-name">' + tier.name + '</span>';
btn.addEventListener('click', function () {
var wasSelected = selectedTierIdx === idx;
root.querySelectorAll('.pp-tier').forEach(function (t) { t.classList.remove('is-selected'); });
selectedTierIdx = wasSelected ? -1 : idx;
if (!wasSelected) btn.classList.add('is-selected');
if (selectedTierIdx === -1) {
assocNote.innerHTML = 'Tap a tier to see its associativity.';
} else {
assocNote.innerHTML = '<strong>' + tier.name + '</strong> (' + tier.ops.join(' ') + ') runs <strong>' + tier.assoc + '</strong>.';
}
renderCurrentFrame();
});
ladder.appendChild(btn);
});
/* ---------------- worked examples ---------------- */
var examples = [
{
label: '2 + 3 * 4',
plain: '2 + 3 * 4',
steps: [
{ expr: '2 + <mark>3 * 4</mark>', caption: '* runs before + — higher precedence.' },
{ expr: '2 + <mark>12</mark>', caption: 'Substitute the result back in.' }
],
result: '14'
},
{
label: '2 ** 3 ** 2',
plain: '2 ** 3 ** 2',
steps: [
{ expr: '2 ** <mark>3 ** 2</mark>', caption: '** is right-to-left — rightmost pair goes first.' },
{ expr: '<mark>2 ** 9</mark>', caption: 'Substitute the result back in.' }
],
result: '512'
},
{
label: '10 - 4 - 2',
plain: '10 - 4 - 2',
steps: [
{ expr: '<mark>10 - 4</mark> - 2', caption: '- is left-to-right — leftmost pair goes first.' },
{ expr: '<mark>6 - 2</mark>', caption: 'Substitute the result back in.' }
],
result: '4'
},
{
label: '20 / 4 * 2',
plain: '20 / 4 * 2',
steps: [
{ expr: '<mark>20 / 4</mark> * 2', caption: '/ and * share precedence — left to right.' },
{ expr: '<mark>5.0 * 2</mark>', caption: 'Substitute the result back in.' }
],
result: '10.0'
},
{
label: '3+4 > 2 and 5<10',
plain: '3 + 4 > 2 and 5 < 10',
steps: [
{ expr: '<mark>3 + 4</mark> > 2 and 5 < 10', caption: 'Arithmetic runs before comparisons.' },
{ expr: '<mark>7 > 2</mark> and 5 < 10', caption: 'First comparison resolves.' },
{ expr: 'True and <mark>5 < 10</mark>', caption: 'Second comparison resolves.' },
{ expr: '<mark>True and True</mark>', caption: 'and runs last — after every comparison.' }
],
result: 'True'
},
{
label: 'not 3 > 5',
plain: 'not 3 > 5',
steps: [
{ expr: 'not <mark>3 > 5</mark>', caption: 'Comparison happens before not.' },
{ expr: '<mark>not False</mark>', caption: 'Substitute the result back in.' }
],
result: 'True'
}
];
var exRow = root.querySelector('#ppExRow');
var exprLine = root.querySelector('#ppExprLine');
var caption = root.querySelector('#ppCaption');
var progress = root.querySelector('#ppProgress');
var nextBtn = root.querySelector('#ppNext');
var backBtn = root.querySelector('#ppBack');
var restartBtn = root.querySelector('#ppRestart');
var currentExIdx = 0;
var currentStep = -1; // -1 = plain, 0..steps.length-1 = steps, steps.length = final
function escapeHtml(s) {
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
function highlightForTier(plainText) {
if (selectedTierIdx === -1) return escapeHtml(plainText);
var ops = tiers[selectedTierIdx].ops.filter(function (o) { return o !== 'unary +' && o !== 'unary -' && o !== '(' && o !== ')'; });
if (ops.length === 0) return escapeHtml(plainText);
var sorted = ops.slice().sort(function (a, b) { return b.length - a.length; });
var escaped = escapeHtml(plainText);
sorted.forEach(function (op) {
var isWord = /^[a-z]+$/i.test(op);
if (isWord) {
var re = new RegExp('\\b' + op + '\\b', 'g');
escaped = escaped.replace(re, function (m) { return '<mark>' + m + '</mark>'; });
} else {
var pattern = op.split('').map(function (c) { return c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }).join('\\s*');
var re2 = new RegExp(pattern, 'g');
escaped = escaped.replace(re2, function (m) { return '<mark>' + m + '</mark>'; });
}
});
return escaped;
}
function renderCurrentFrame() {
var ex = examples[currentExIdx];
if (currentStep === -1) {
exprLine.innerHTML = highlightForTier(ex.plain);
caption.textContent = 'Tap Next to see the evaluation order.';
} else if (currentStep < ex.steps.length) {
exprLine.innerHTML = ex.steps[currentStep].expr;
caption.textContent = ex.steps[currentStep].caption;
} else {
exprLine.innerHTML = '= <span class="final-val">' + ex.result + '</span>';
caption.textContent = 'Fully evaluated.';
}
progress.textContent = 'Step ' + (currentStep + 2) + ' of ' + (ex.steps.length + 2);
backBtn.disabled = currentStep === -1;
nextBtn.disabled = currentStep >= ex.steps.length;
nextBtn.textContent = currentStep >= ex.steps.length ? 'Done' : 'Next →';
}
function selectExample(idx) {
currentExIdx = idx;
currentStep = -1;
root.querySelectorAll('.pp-ex-btn').forEach(function (b, i) { b.classList.toggle('is-selected', i === idx); });
renderCurrentFrame();
}
examples.forEach(function (ex, idx) {
var btn = document.createElement('button');
btn.className = 'pp-ex-btn' + (idx === 0 ? ' is-selected' : '');
btn.textContent = ex.label;
btn.addEventListener('click', function () { selectExample(idx); });
exRow.appendChild(btn);
});
nextBtn.addEventListener('click', function () {
var ex = examples[currentExIdx];
if (currentStep < ex.steps.length) {
currentStep++;
renderCurrentFrame();
}
});
backBtn.addEventListener('click', function () {
if (currentStep > -1) {
currentStep--;
renderCurrentFrame();
}
});
restartBtn.addEventListener('click', function () {
currentStep = -1;
renderCurrentFrame();
});
renderCurrentFrame();
})();
</script>
</div>