Python Literals, Variables and Number Systems
PCEP objective 1.3 covers literals, variables and numeral systems, and it is the first objective with real traps in it. Binary, octal and hexadecimal literals all have prefixes and one of them changed in Python 3; `bool` is a subclass of `int`, so `True + True` is 2; and the rules for a legal variable name are asked as a spot-the-error question. Everything here is one line of code to check.
Language Fundamentals Guide 6 of 26 Beginner
- Python3.14.4
- OSUbuntu 26.04 LTS
- pip25.1.1
- TimeAbout 15 min
- Reviewed23 August 2026
Written against the versions above. Written against **Python 3.14**. Two things in this objective are Python 3 changes that older material gets wrong: octal is `0o17` and a bare leading zero is now an error, and underscores in numeric literals (`1_000_000`) have been legal since 3.6.
| 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 5 - names cannot collide with keywords.
- Nothing else.
-
Every literal type, and what each one produces
A literal is a value written directly in the source. A variable is a name bound to a value. The distinction matters in questions phrased as "which of these is a literal?".
Below is one literal of each type the syllabus can draw on, printed with the class it produces.
repr()is used rather thanstrso quotes stay visible.bash Example session cat > ~/py/littypes.py <<'PY'for value in (1, 1.0, "1", True, None, 1j, (1,), [1], {1}, {"a": 1}): print(repr(value).ljust(8), "->", type(value).__name__)PYpython3 ~/py/littypes.py1 -> int1.0 -> float'1' -> strTrue -> boolNone -> NoneType1j -> complex(1,) -> tuple[1] -> list{1} -> set{'a': 1} -> dictExpected resultTen literals, ten type names - and
Truereportsbool, notint.Success conditionYou can name the type any literal in the syllabus produces.
-
Four numeral systems, three prefixes
Integers can be written in binary, octal, hexadecimal or decimal. The prefixes are case-insensitive (
0X1Fworks) and the letters in a hex literal are too.Three separate things are going on and questions mix them deliberately: literals in the source, converting a number to text in a given base, and parsing text from a given base.
bash Example session cat > ~/py/numerals.py <<'PY'print("literals :", 0b1010, 0o17, 0x1f, 10)print("to text :", bin(10), oct(15), hex(31))print("from text:", int("1010", 2), int("17", 8), int("ff", 16))PYpython3 ~/py/numerals.pyliterals : 10 15 31 10to text : 0b1010 0o17 0x1ffrom text: 10 15 255Expected result
10 15 31 10, then the prefixed strings, then10 15 255.Success conditionYou can move between all four bases in both directions.
-
The octal literal that used to work
In Python 2, a leading zero meant octal:
017was 15. Python 3 requires the0oprefix and rejects the bare leading zero outright, because017looked far too much like the number seventeen.bash Example session python3 -c 'print(017)' File "<string>", line 1 print(017) ^SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers[exit 1]Expected result
SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers.Success conditionYou can spot a Python 2 octal literal in a question.
-
Float literals, and making long numbers readable
A float literal needs either a decimal point or an exponent, and it can be surprisingly sparse:
.5and5.are both valid.The exponent form uses
eorEand always produces a float, even when the value is a whole number -3e2is300.0, not300.Underscores may be placed between digits purely for readability, in any base.
bash Example session cat > ~/py/floatlit.py <<'PY'print(.5, 5., 1.0)print(3e2, 1E-3, 6.02e23)print(1_000_000, 0b1010_1010, type(1_000_000).__name__)PYpython3 ~/py/floatlit.py0.5 5.0 1.0300.0 0.001 6.02e+231000000 170 intExpected result
0.5 5.0 1.0, then300.0 0.001 6.02e+23, then1000000 170 int.Success conditionYou can recognise every float literal form the syllabus uses.
-
bool is a kind of int, and it shows
boolis a subclass ofint.Trueis 1 andFalseis 0, not merely convertible to them, and every arithmetic operator therefore accepts them.This is one of the highest-yield facts in objective 1.3, because it makes
True + Truea legal expression with a numeric answer.bash Example session cat > ~/py/boolint.py <<'PY'print(True + True)print(isinstance(True, int))print(int(True), int(False))print(True == 1, True is 1)PYpython3 ~/py/boolint.py/home/sysadmin/py/boolint.py:4: SyntaxWarning: "is" with 'int' literal. Did you mean "=="? print(True == 1, True is 1)2True1 0True FalseExpected result
2,True,1 0, thenTrue False.Success conditionYou can predict the result of arithmetic on booleans.
-
What makes a legal variable name
The rules are short: letters, digits and underscores; not starting with a digit; not a keyword; case-sensitive. All four names below are legal, including the two that look questionable.
bash Example session cat > ~/py/names.py <<'PY'_leading = 1trailing_ = 2with2digits = 3UPPER = 4print(_leading, trailing_, with2digits, UPPER)PYpython3 ~/py/names.py1 2 3 4Expected result
1 2 3 4- every one of those names is valid.Success conditionYou can tell a legal name from an illegal one without guessing.
-
The two ways a name goes wrong
A name starting with a digit fails at compile time, with an error message that is less obvious than you would expect.
A name that is legal but never assigned fails at runtime, with a
NameError- the distinction from guide 4, in the smallest possible example.bash Example session python3 -c '2var = 1' File "<string>", line 1 2var = 1 ^SyntaxError: invalid decimal literal[exit 1]python3 -c 'print(counter)'Traceback (most recent call last): File "<string>", line 1, in <module> print(counter) ^^^^^^^NameError: name 'counter' is not defined[exit 1]Expected result
SyntaxError: invalid decimal literal, thenNameError: name 'counter' is not defined.Success conditionYou can name both failures and say which is found before the program runs.
-
What the exam does with this objective
Objective 1.3 is part of the 18% fundamentals block and produces a recognisable set of questions:
"What is the value of
0x1f?" - convert. 31."What does
017print?" - aSyntaxError."What is
True + True?" - 2."Which of these is not a valid variable name?" - look for a leading digit, a keyword, or a hyphen.
"What type is
3e2?" - float."What does
bin(10)return?" - the string'0b1010', not the number.None of these needs anything memorised beyond the three prefixes, and all of them can be checked in one line at the prompt.
guide 7 is next, and it is where the fundamentals block gets genuinely dense.
bash rm -f ~/py/littypes.py ~/py/numerals.py ~/py/floatlit.py ~/py/boolint.py ~/py/names.py && ls -A ~/pyExpected resultAn empty scratch directory.
Success conditionYou can answer every question shape this objective produces.
Troubleshooting
SyntaxError: invalid decimal literalon what looks like a variable name.Why: The name starts with a digit, so the lexer reads it as a malformed number.
Fix:Rename it.
var2is fine;2varcannot be. The message mentions a literal because the lexer never reached the point of treating it as a name.SyntaxError: leading zeros in decimal integer literals are not permitted.Why: Python 2 octal syntax -
017- or a zero-padded number.Fix:Use
0o17for octal. For zero-padded display use a format string:f"{n:03d}".bin(10) + 1raises a TypeError.Why:
bin(),oct()andhex()return strings, not numbers.Fix:Convert back if you need arithmetic:
int(bin(10), 2) + 1. Or keep the integer and only format it for display.SyntaxWarning: "is" with 'int' literal.Why:
iscompares identity, not value, and was almost certainly not what was meant.Fix:Use
==for values and reserveisforNoneand other singletons. The warning is a 3.8+ diagnostic; the behaviour it warns about is unchanged and is examinable.NameErroron a variable that is assigned later in the file.Why: Python has no declarations - a name exists from the moment it is assigned, in execution order.
Fix:Assign before use. If the use is inside a function that is called later, that is fine; if it is at module level above the assignment, it is not.