Numbers: subnet math without a calculator tab
Python has two numeric types you’ll use constantly: integers (int) for
things you count — ports, VLANs, prefix lengths — and floats (float)
for things you measure — utilization, error rates. The operators are mostly
what you’d expect, plus three that matter more in networking than anywhere
else:
>>> 2 ** (32 - 24) - 2 # ** is exponent: usable hosts in a /24
254
>>> 48 // 12 # // is integer division (no remainder)
4
>>> 530 % 48 # % is modulo: the remainder
2
That first line is the one to tattoo somewhere: a /26 has
2 ** (32 - 26) - 2 = 62 usable addresses, and now you never count on your
fingers during a design review again. Division with / always returns a
float, which pairs with round() for human-readable reporting:
>>> used, total = 87, 254
>>> round(used / total * 100, 1)
34.3
Files: configs live on disk, not in single lines
Lesson 1 parsed one line at a time. Real work starts with whole files —
saved configs, inventory exports, show output you redirected last night.
The professional pattern is the with statement:
with open("den-acc-sw03.cfg") as f:
config = f.read()
lines = config.splitlines()
with guarantees the file gets closed when the block ends — including when
your code crashes halfway through. The bare f = open(...) form you’ll see
in old scripts leaks file handles when things go wrong; just don’t.
Two reading styles cover nearly everything:
f.read()— the whole file as one string. Pair with.splitlines()to get a clean list of lines with no trailing\ncharacters.f.readlines()— a list of lines with their newlines still attached. Usually the wrong choice for exactly that reason.
Writing is the same shape with a mode argument:
with open("pre-check-den-acc-sw03.txt", "w") as f:
f.write(version_output)
Lists: the container you’ll use every single day
A list is an ordered, changeable collection — and almost everything in network automation arrives as one: lines of a config, interfaces on a switch, devices in a site.
>>> uplinks = ["Gi1/0/1", "Gi1/0/2", "Te1/1/1", "Te1/1/2"]
>>> len(uplinks)
4
>>> uplinks[0] # indexing starts at ZERO
'Gi1/0/1'
>>> uplinks[-1] # negatives count from the end
'Te1/1/2'
Slicing pulls out sub-lists with [start:stop] — start included, stop
excluded:
>>> uplinks[0:2]
['Gi1/0/1', 'Gi1/0/2']
>>> uplinks[-2:] # everything from the second-to-last on: the tens
['Te1/1/1', 'Te1/1/2']
Omitting an endpoint means “from the beginning” or “to the end” — so
lines[:10] is the first ten lines of a config and lines[-5:] is the last
five. Lists grow and reorganize in place:
>>> vlans = [10, 30, 20]
>>> vlans.append(40) # add one item to the end
>>> vlans
[10, 30, 20, 40]
>>> sorted(vlans) # returns a NEW sorted list
[10, 20, 30, 40]
>>> vlans # the original is untouched
[10, 30, 20, 40]
And because a config is just a list of strings once you .splitlines() it,
everything from Lesson 1 composes with everything above:
Output appears here. First run downloads the Python runtime (~10 MB), so give it a few seconds.
Exercises (graded)
cd labs/python-foundations/lesson02
pytest -q
Five functions in exercises.py:
usable_hosts(prefix_len)— usable host count for a prefix lengthutilization_pct(used, total)— percentage utilization, one decimal placeread_config_lines(path)— a file’s lines as a clean list (no newlines)get_uplinks(interfaces)— the last two interfaces in a listadd_vlan(vlans, vlan_id)— a new sorted list with the VLAN added
Reference solutions in solutions/ — after an honest attempt.
A /26 subnet — how many usable host addresses, and which expression computes it?
lines = config.splitlines() gives a 412-line config. What is lines[-2:]?
After vlans = [30, 10, 20] and vlans = vlans.sort(), what is vlans?
Summary
Integer math (**, //, %) handles subnet sizing — 2 ** (32 - prefix) - 2
— and floats with round() handle utilization reporting, but only after you
int() whatever you parsed, because parsed text is always strings. The with open(...) statement is the only file-reading pattern worth learning, and
.read().splitlines() hands you a config as a clean list. Lists index from
zero, slice with [start:stop], grow with .append(), and hide one classic
trap: in-place methods like .sort() return None. Next lesson:
conditionals and loops — where your scripts finally start making decisions
about every line in that config, not just the ones you point at.