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
- OSUbuntu 26.04 LTS
- Python3.14.4
- pip25.1.1
- TimeAbout 14 min
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 |
This guide includes
Use this for the objective about the shape of a Python program. This matters because there are two keyword lists rather than one - and three distinct indentation errors, only one of which has an exception of its own.
- printing the keyword list rather than reciting it, including the soft keywords
- seeing what using a keyword as a name actually produces
- meeting all three indentation errors, including the one with its own exception
- finding how much indentation is required, and what does and does not count as a comment
- putting more than one statement on a line, and filling an empty block
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.