Python Keywords, Indentation and Comments
PCEP objective 1.2 covers the shape of a Python program: which words are reserved, how blocks are marked, and what a comment is. Indentation is where the marks go - there are three different errors it can produce and one of them has its own exception class, `TabError`, which practice questions love. The keyword list is worth printing rather than memorising from a textbook, because it has changed.
Language Fundamentals Guide 5 of 26 Beginner
- Python3.14.4
- OSUbuntu 26.04 LTS
- pip25.1.1
- TimeAbout 14 min
- Reviewed23 August 2026
Written against the versions above. 35 keywords and 4 **soft** keywords on Python 3.14. Older material lists 33 or 35 with `async`/`await` missing or `print` present; both are wrong for current Python. Soft keywords (`match`, `case`, `type`, `_`) are newer than the syllabus and are reserved only in specific positions.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| RUNNER01 | 192.168.0.27 | Ubuntu 26.04 LTS | Python 3.14.4 - the only machine this path needs | 2 Core | 4 GB | 50 GB |
Before you start
- guide 4 - you should know why a syntax error prints nothing.
- Nothing else. Every sample here is under six lines.
-
The keyword list, printed rather than recited
Keywords are the reserved words of the language - the lexis from the previous guide. You cannot use one as a variable name, and the
keywordmodule will tell you the list for the interpreter you actually have, which is more reliable than any textbook.bash Example session python3 -c 'import keyword; print(len(keyword.kwlist)); print(keyword.kwlist)'35['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']Expected result35, and the list itself.
Success conditionYou have the authoritative keyword list for your own interpreter.
-
Soft keywords, which older material does not mention
There is a second, shorter list. A soft keyword is reserved only where the grammar needs it to be, and is a perfectly ordinary name everywhere else.
bash Example session python3 -c 'import keyword; print(keyword.softkwlist)'['_', 'case', 'match', 'type']Expected result
['_', 'case', 'match', 'type'].Success conditionYou know why
match = 1is legal andclass = 1is not. -
What using a keyword as a name actually gives you
A hard keyword in a name position is a syntax error, found at compile time - which, from the previous guide, means the file produces no output at all.
bash Example session python3 -c 'class = 1' File "<string>", line 1 class = 1 ^SyntaxError: invalid syntax[exit 1]Expected result
SyntaxError: invalid syntax, with the caret on the=.Success conditionYou can recognise the error a reserved word produces.
-
Indentation error one: no block where one was required
Python marks blocks with indentation rather than braces. A statement ending in
:must be followed by an indented block.cat -Ais used here to make the whitespace visible -$marks each line ending, so you can see there is no leading space on line 2.bash Example session printf 'if True:\nprint("a")\n' > ~/py/i1.py && cat -A ~/py/i1.pyif True:$print("a")$python3 ~/py/i1.py File "/home/sysadmin/py/i1.py", line 2 print("a") ^^^^^IndentationError: expected an indented block after 'if' statement on line 1[exit 1]Expected result
IndentationError: expected an indented block after 'if' statement on line 1.Success conditionYou can read an IndentationError back to the statement that opened the block.
-
Indentation error two: a block that was not asked for
The opposite mistake. Line 2 is correctly indented under the
if; line 3 is indented *further*, opening a block that nothing introduced.bash Example session printf 'if True:\n print("a")\n print("b")\n' > ~/py/i2.py && python3 ~/py/i2.py File "/home/sysadmin/py/i2.py", line 3 print("b")IndentationError: unexpected indent[exit 1]Expected result
IndentationError: unexpected indent, on line 3.Success conditionYou can distinguish a missing block from a surplus one by the message alone.
-
Indentation error three: the one with its own exception
Mixing tabs and spaces is the interesting case. Below, line 2 is indented with a tab and line 3 with eight spaces - which in most editors look identical.
cat -Ashows the difference:^Iis a tab.bash Example session printf 'if True:\n\tprint("tab")\n print("spaces")\n' > ~/py/i3.py && cat -A ~/py/i3.pyif True:$^Iprint("tab")$ print("spaces")$python3 ~/py/i3.py File "/home/sysadmin/py/i3.py", line 3 print("spaces")TabError: inconsistent use of tabs and spaces in indentation[exit 1]Expected result
TabError: inconsistent use of tabs and spaces in indentation.Success conditionYou can name the third indentation exception, which is not
IndentationError. -
How much indentation is required
None specifically. Python cares that a block is indented consistently, not how far. Two spaces is legal, and so is eleven.
bash Example session cat > ~/py/i4.py <<'PY'if True: print("two spaces is legal") print("as long as the block agrees")PYpython3 ~/py/i4.pytwo spaces is legalas long as the block agreesExpected resultBoth lines print. Two spaces is a perfectly valid indent.
Success conditionYou know the rule is consistency within a block, not a fixed width.
-
Comments, and the two things people think are comments
#starts a comment and it runs to the end of the line. There is no multi-line comment syntax in Python at all.Three lines below test the edges: a whole-line comment, a trailing comment, a
#inside a string, and a triple-quoted string sitting on its own.bash Example session cat > ~/py/comments.py <<'PY'# a whole-line commentprint("code") # a trailing commentprint("# this is not a comment")"""A string on its own line is an expression, not a comment.Python evaluates it and throws the value away."""PYpython3 ~/py/comments.pycode# this is not a commentExpected result
code, then# this is not a comment. The triple-quoted string produces nothing.Success conditionYou know what
#does and does not do. -
One statement per line, and the two ways round it
Python's default unit is the line. Two mechanisms bend that, and both are examinable.
A semicolon puts two statements on one line. It is legal and discouraged.
Line joining splits one statement across several: explicitly with a trailing backslash, or implicitly - and far better - inside any unclosed bracket, where no backslash is needed at all.
bash Example session cat > ~/py/joining.py <<'PY'a = 1; b = 2print(a, b) total = 1 + \ 2print(total) nums = [1, 2, 3]print(nums)PYpython3 ~/py/joining.py1 23[1, 2, 3]Expected result
1 2, then3, then[1, 2, 3].Success conditionYou can read a statement that spans lines and one that shares a line.
-
The empty block, and what fills it
Since a
:demands an indented block, there has to be a way to write a block that does nothing. That ispass- a real statement whose entire purpose is to be syntactically present.bash Example session cat > ~/py/emptyblock.py <<'PY'if True: passprint("an empty block still needs a statement")PYpython3 ~/py/emptyblock.pyan empty block still needs a statementExpected resultThe message prints; the
ifbody did nothing.Success conditionYou can satisfy the compiler without writing behaviour.
-
Leaving the directory as you found it
Seven scratch files were written on this page. Every guide in this path removes what it wrote, so
~/pyis empty again before the next one.guide 6 is next: objective 1.3, and the first place the exam sets a genuine trap.
bash rm -f ~/py/i1.py ~/py/i2.py ~/py/i3.py ~/py/i4.py ~/py/comments.py ~/py/joining.py ~/py/emptyblock.py && ls -A ~/pyExpected resultAn empty listing.
Success conditionNothing left behind.
Troubleshooting
TabError: inconsistent use of tabs and spaces in indentation.Why: Some lines in the block are indented with tabs and others with spaces.
Fix:Convert the file to spaces only - four per level.
cat -A file.pyshows tabs as^Iso you can see which lines are guilty. Configure your editor to insert spaces and the error cannot recur.IndentationError: expected an indented blockon a block that has only a comment in it.Why: Comments are discarded by the lexer, so the block really is empty.
Fix:Add
pass. A comment is not a statement and cannot satisfy a:.SyntaxErroron a line that looks correct, with the caret on the next line or at the end of the file.Why: An unclosed bracket or quote earlier. Implicit line joining swallows everything until the bracket closes.
Fix:Look upwards from the reported line for an unmatched
(,[,{or quote. Python 3.10 and later usually name it -'(' was never closed- and point at where it opened.SyntaxError: unexpected character after line continuation character.Why: A space or tab after a trailing backslash. The backslash must be the last character on the line.
Fix:Delete the trailing whitespace, or better, wrap the expression in brackets and drop the backslash entirely.
A practice test says
matchcannot be used as a variable name.Why: The test predates soft keywords, or confuses them with reserved words.
Fix:
match,case,typeand_are soft keywords - reserved only in the grammar positions that need them.match = 1is valid Python.