PCAP cheat sheet
What PCAP-31-03 adds to PCEP, grouped by its five sections - modules, exceptions, strings, object-oriented programming and the miscellaneous 22%. Weighted the way the exam is: OOP gets the most rows because it is 34% of the paper.
- Python3.14.4
- OSUbuntu 26.04 LTS
- pip25.1.1
- Commands62
- Reviewed23 August 2026
Section 1: Modules and packages (12%)
-
import · import as · from import · from import as · from import *Five forms, five different sets of names bound. That difference is the whole objective.
bash Example session python3 ~/py/importforms.pyimport math -> 3.141592653589793import math as m -> 2.718281828459045from math import pi -> 3.141592653589793with as, several -> 3.141592653589793 2.718281828459045from math import * -> 4.0 2 -
from math import pi, then math.piNameError - not ImportError. The import succeeded; the name `math` was never bound.
bash Example session python3 ~/py/frombroken.py3.141592653589793Traceback (most recent call last): File "/home/sysadmin/py/frombroken.py", line 4, in <module> print(math.pi) ^^^^NameError: name 'math' is not defined. Did you forget to import 'math'?[exit 1] -
a missing module vs a missing nameModuleNotFoundError for the module, ImportError for a name inside one. The first is a subclass of the second.
bash Example session python3 -c 'import nosuchmodule'Traceback (most recent call last): File "<string>", line 1, in <module> import nosuchmoduleModuleNotFoundError: No module named 'nosuchmodule'[exit 1] -
a module body runs onceThree import statements, one execution. Python caches it in sys.modules.
bash Example session cd ~/py/impdemo && python3 twice.pythe module body is runningvalue is 1 -
__name__ == "__main__"`"__main__"` when run directly, the module name when imported.
bash Example session cd ~/py/impdemo && python3 dual.py__name__ is __main__started directly -
ceil · floor · trunc · roundFour directions, and they disagree on negatives. floor(-2.9) is -3; trunc(-2.9) is -2.
bash Example session python3 ~/py/mathround.py 2.1 ceil 3 | floor 2 | trunc 2 | round 2 2.9 ceil 3 | floor 2 | trunc 2 | round 3 -2.1 ceil -2 | floor -3 | trunc -2 | round -2 -2.9 ceil -2 | floor -3 | trunc -2 | round -3 -
math.pow vs **math.pow always returns a float; `**` on two ints returns an int. Almost everything in math returns a float.
bash Example session python3 ~/py/mathpow.pysqrt(16) 4.0 floatpow(2, 10) 1024.0 float2 ** 10 1024 intexp(1) 2.718281828459045log(e) 1.0log(8, 2) 3.0log10(1000) 3.0hypot(3, 4) 5.0factorial(5) 120gcd(12, 18) 6fabs(-3) 3.0 | abs(-3) 3 -
sin() takes radiansSo sin(90) is not 1. `math.radians(90)` first.
bash Example session python3 ~/py/mathtrig.pysin(pi / 2) 1.0cos(0) 1.0sin(90) 0.8939966636 <- 90 radians, not degreesradians(180) 3.141592653589793degrees(pi) 180.0sin(radians(90)) 1.0 -
random.seed(n)The same seed gives the same sequence, every run. Which is why every random example in this path is seeded.
bash Example session python3 ~/py/randseed.pyfirst run with seed 1 : [18, 73, 98, 9, 33]again with seed 1 : [18, 73, 98, 9, 33]with seed 2 : [8, 12, 11, 47, 22] -
randint vs randrangerandint(1, 3) can return 3. randrange(1, 3) cannot - it follows range semantics.
bash Example session python3 ~/py/randbounds.pyrandint(1, 3) produced [1, 2, 3]randrange(1, 3) produced [1, 2] -
shuffle vs sampleshuffle reorders in place and returns None; sample returns a new list. `sample(xs, len(xs))` is a shuffled copy.
bash Example session python3 ~/py/randinplace.pyshuffle returns None and xs is now [1, 3, 4, 5, 2]sample returns [5, 1, 3] and ys is still [1, 2, 3, 4, 5] -
platform.system() · machine() · node()OS name, architecture, hostname. system() returns "Darwin" on macOS. processor() is often an empty string.
bash Example session python3 ~/py/platformall.pyplatform 'Linux-7.0.0-30-generic-x86_64-with-glibc2.43'system 'Linux'machine 'x86_64'processor ''node 'ahm-runner01'release '7.0.0-30-generic'version '#30-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 31 18:22:54 UTC 2026'python_implementation 'CPython'python_version '3.14.4'python_version_tuple ('3', '14', '4')python_build ('main', 'Jun 18 2026 14:25:02')python_compiler 'GCC 15.2.0' -
import pkg does not import pkg.modAttributeError. Use `import pkg.mod`, or `from . import mod` inside __init__.py.
The most asked question in objective 1.5.
bash Example session cd ~/py/proj && python3 nosub.pyshapes/__init__.py ran1.0Traceback (most recent call last): File "/home/sysadmin/py/proj/nosub.py", line 4, in <module> print(shapes.circle.area(2)) ^^^^^^^^^^^^^AttributeError: module 'shapes' has no attribute 'circle'[exit 1]
Section 2: Exceptions (14%)
-
a bare raiseRe-raises the exception currently being handled, keeping its original traceback. Outside a handler it is a RuntimeError.
bash Example session python3 ~/py/reraise.pylogging, then re-raisingthe outer handler got it too: division by zero -
assert (cond, "msg")Always passes. The brackets make a two-item tuple, which is truthy. Write `assert cond, "msg"`.
bash Example session python3 ~/py/asserttuple.py/home/sysadmin/py/asserttuple.py:1: SyntaxWarning: assertion is always true, perhaps remove parentheses? assert (1 == 2, "this message is part of a tuple")no AssertionError was raised -
python3 -ORemoves every assertion from the compiled code. Never use assert to validate input.
bash Example session python3 -O ~/py/assertbare.py && echo "exit 0 - the assertion was removed by -O"exit 0 - the assertion was removed by -O -
return inside finallyDiscards the exception entirely - no handler, no traceback. Python 3.14 warns; the behaviour is unchanged and examinable.
bash Example session python3 ~/py/finallyswallow.py/home/sysadmin/py/finallyswallow.py:5: SyntaxWarning: 'return' in a 'finally' block return "finally swallowed it"finally swallowed it -
e.args and str(e)One argument gives that string. Several give the repr of the whole tuple. None gives an empty string.
bash Example session python3 ~/py/excargs.pyraise ValueError('one message',) -> args ('one message',) | str() 'one message'raise ValueError('a', 'b', 3) -> args ('a', 'b', 3) | str() "('a', 'b', 3)"raise ValueError() -> args () | str() '' -
except ... as eThe name is deleted when the clause ends, to release the traceback. Copy it inside the block if you need it after.
bash Example session python3 ~/py/excscope.pyinside the clause: division by zeroTraceback (most recent call last): File "/home/sysadmin/py/excscope.py", line 5, in <module> print(e) ^NameError: name 'e' is not defined[exit 1] -
a family of exceptionsOne base class, several subclasses, and `except AppError:` catches all of them. That is what objective 2.2 is for.
bash Example session python3 ~/py/customfamily.pycaught NotFound with the AppError clause: alphacaught Forbidden with the AppError clause: betacaught AppError with the AppError clause: gamma -
raising a non-exceptionTypeError: exceptions must derive from BaseException - at run time, not at class definition.
bash Example session python3 ~/py/notanexception.pyTraceback (most recent call last): File "/home/sysadmin/py/notanexception.py", line 5, in <module> raise Bad()TypeError: exceptions must derive from BaseException[exit 1]
Section 3: Strings (18%)
-
s.encode() / b.decode()"café" is 4 characters and 5 UTF-8 bytes. There is no str.decode() in Python 3.
bash Example session python3 ~/py/strbytes.pythe str 'café' str len 4the bytes b'caf\xc3\xa9' bytes len 5decoded back 'café's == b.decode() True -
the same bytes, two encodingsUTF-8 bytes read as latin-1 give `café`. No error - latin-1 can decode any byte.
bash Example session python3 ~/py/mojibake.pyas utf-8 caféas latin-1 café -
b[0] vs b[0:1]Indexing bytes gives an int; slicing gives bytes. And bytes never mix with str.
bash Example session python3 ~/py/bytescontents.pyrepr b'abc'len 3b[0] 97 intb[0:1] b'a' byteslist(b) [97, 98, 99]bytes([65, 66, 67]) b'ABC' -
ord() and chr()Space 32, '0' 48, 'A' 65, 'a' 97. Those four numbers derive everything else.
bash Example session python3 ~/py/ordchr.pyord('A') 65ord('a') 97ord('0') 48ord(' ') 32chr(65) Achr(97) achr(8364) €round trip Z -
the gap between Z and aSix punctuation characters. Which is why "Z" < "a" and why sorting mixed case needs a key.
bash Example session python3 ~/py/ranges.pydigits 48 to 57uppercase 65 to 90lowercase 97 to 122the gap between Z and a is 6 characters: ['[', '\\', ']', '^', '_', '`'] -
a caesar shiftSubtract ord("a"), add the shift, `% 26`, add ord("a") back. The `% 26` is what makes it wrap.
bash Example session python3 ~/py/caesar.pyencoded dwwdfn dw gdzqdecoded attack at dawn -
the 24 methods PCAP namesAll of them in one run: case, trim, search, split/join, and the six is-methods.
bash Example session python3 ~/py/pcapmethods.pycapitalize() Hello worldcenter(10,'*') '****hi****'count('l') 3endswith() Truefind('o') 4index('o') 4isalnum() True Falseisalpha() True Falseisdigit() True Falseislower() True Falseisspace() True Falseisupper() True Falsejoin() a-b-clower() hello, worldlstrip() 'x 'replace() HeLLo, WorLdrfind('o') 8rstrip() ' x'split(', ') ['Hello', 'World']startswith() Truestrip() 'x'swapcase() hELLO, wORLDtitle() Hello Worldupper() HELLO, WORLD -
the optional argumentscount and find take a start; replace takes a maximum; endswith takes a tuple; strip("x") strips characters, not a substring.
bash Example session python3 ~/py/methodargs.pycount('l') 3count('l', 4) 1find('o') 4find('o', 5) 8find('zz') -1rfind('o') 8replace('l','L') HeLLo, WorLdreplace('l','L',2) HeLLo, Worldendswith(('x','d')) Truelstrip('x') on xxhixx 'hixx'strip('x') on xxhixx 'hi' -
capitalize vs titleBoth lowercase the rest. title() treats an apostrophe as a word boundary, so o'brien becomes O'Brien.
bash Example session python3 ~/py/caseedges.pycapitalize Mcdonald o'brientitle Mcdonald O'Brienupper MCDONALD O'BRIENswapcase MCdonald O'BRIENistitle True
Section 4: Object-oriented programming (34%)
-
__str__ vs __repr__print() uses __str__; a list of your objects uses __repr__. Define __repr__ if you only define one.
bash Example session python3 ~/py/strrepr.pyprint(p) a point at 1, 2str(p) a point at 1, 2repr(p) Point(1, 2)in a list [Point(1, 2)] -
__init__ returning a valueTypeError: __init__() should return None. A bare `return` is fine.
bash Example session python3 ~/py/ctorreturn.pyTraceback (most recent call last): File "/home/sysadmin/py/ctorreturn.py", line 7, in <module> d = Dog("Rex")TypeError: __init__() should return None, not 'str'[exit 1] -
def __init__(self, items=[])Every instance shares one list. `items=None` and build it inside.
bash Example session python3 ~/py/ctormutable.pya.add('apple') -> ['apple']b.add('pear') -> ['apple', 'pear']a.items is b.items: True -
self in the argument count"takes 1 but 2 were given" counts self. "missing 1 required" does not. Both messages are correct.
bash Example session python3 ~/py/methextra.pyTraceback (most recent call last): File "/home/sysadmin/py/methextra.py", line 7, in <module> print(c.m(1)) ~~~^^^TypeError: C.m() takes 1 positional argument but 2 were given[exit 1] -
obj.m vs Class.mA bound method against a plain function. `Class.m(obj)` is exactly `obj.m()`.
bash Example session python3 ~/py/boundmethod.pythrough the class <function Counter.bump at 0x73b4a6baa610>through an instance <bound method Counter.bump of <__main__.Counter object at 0x73b4a6b90d70>>c.bump() 1Counter.bump(c) 2the same object? True -
a.total = 100 on a class variableCreates an instance variable that shadows it. The class variable is unchanged. Reading searches instance then class; writing always hits the instance.
The one rule the whole 34% section turns on.
bash Example session python3 ~/py/shadowclassvar.pyCounter.total 0a.total 100b.total 0a.__dict__ {'total': 100}b.__dict__ {} -
a mutable class variableShared by every instance, and `a.__dict__` is empty - which is the proof. append is a read followed by a mutation, not an assignment.
bash Example session python3 ~/py/mutableclassvar.pya.contents ['apple', 'pear']b.contents ['apple', 'pear']Basket.contents ['apple', 'pear']a.contents is b.contents: Truea.__dict__ {} -
self.made += 1Reads from the class, writes to the instance. Every instance says 1 and the class stays 0. Use `ClassName.made += 1`.
bash Example session python3 ~/py/badcount.pyWidget.made 0each instance says [1, 1, 1]ws[0].__dict__ {'name': 'a', 'made': 1} -
self.__privateRenamed to `_ClassName__private` at compile time. Not private - reachable by anyone who knows the rule.
bash Example session python3 ~/py/mangled.pythe name in __dict__: ['_Account__private']reachable as _Account__private: 3so it is not private, only renamed -
why mangling existsCollision avoidance, not privacy. A base and a subclass can each keep their own `self.__x`.
bash Example session python3 ~/py/manglepurpose.pyBase.base_sees() base valueChild.child_sees() child valueboth stored: ['_Base__x', '_Child__x'] -
__class__ · __name__ · __bases__ · __mro____bases__ is the direct parents; __mro__ is the full lookup order, ending at object. Both are tuples.
bash Example session python3 ~/py/whatami.pytype(c) <class '__main__.Child'>c.__class__ <class '__main__.Child'>its name ChildChild.__name__ ChildChild.__bases__ (<class '__main__.Base'>,)Child.__mro__ ['Child', 'Base', 'object'] -
__doc__ is not inheritedA subclass with no docstring of its own has None, not the parent's. Every other class attribute does fall through.
bash Example session python3 ~/py/docnotinherited.pyBase.__doc__ 'A base class with a docstring.'Child.__doc__ None -
isinstance vs type() isisinstance accepts subclasses; `type(x) is C` demands an exact match. Use isinstance.
bash Example session python3 ~/py/isinst.pyisinstance(c, Child) Trueisinstance(c, Base) Trueisinstance(c, object) Truetype(c) is Child Truetype(c) is Base Falseissubclass(Child, Base) Trueissubclass(Base, Child) Falseisinstance(True, int) True -
a subclass __init__ without super()Constructs happily, missing an attribute, and fails somewhere else later. `super().__init__(...)` first.
bash Example session python3 ~/py/ctorforget.pyGood(1, 2).__dict__ {'a': 1, 'b': 2}Bad(1, 2).__dict__ {'b': 2}Traceback (most recent call last): File "/home/sysadmin/py/ctorforget.py", line 19, in <module> print(Bad(1, 2).a) ^^^^^^^^^^^AttributeError: 'Bad' object has no attribute 'a'[exit 1] -
super().method()The next class along the MRO - one step, not to the root.
bash Example session python3 ~/py/superextend.pyAnimal ...Dog WoofPuppy Woof!mro ['Puppy', 'Dog', 'Animal', 'object'] -
the diamondD(B, C) with only C defining the method resolves to C, not A - because A comes after both in the MRO.
bash Example session python3 ~/py/diamond2.pyD().who() CD.__mro__ ['D', 'B', 'C', 'A', 'object'] -
super() in a diamondB's super() goes to C, its sibling - not to A, its base. super() follows the instance's MRO.
bash Example session python3 ~/py/supermro.pyD -> B -> C -> A['D', 'B', 'C', 'A', 'object'] -
class C(A, B) after class B(A)TypeError: cannot create a consistent MRO - at class-creation time. List the most derived base first.
bash Example session python3 ~/py/badmro.pyTraceback (most recent call last): File "/home/sysadmin/py/badmro.py", line 9, in <module> class C(A, B): passTypeError: Cannot create a consistent method resolution order (MRO) for bases A, B[exit 1]
Section 5: Comprehensions, lambdas, closures, I/O (22%)
-
[expr for x in xs if cond]A trailing `if` filters; an `if`/`else` in the expression transforms. Count the outputs to tell which.
bash Example session python3 ~/py/compparts.pyexpression only [0, 1, 4, 9, 16]with a filter [0, 2, 4, 6, 8]expression with if [0, -1, 2, -3, 4]two for clauses [('a', 1), ('a', 2), ('b', 1), ('b', 2)]flattening [1, 2, 3, 4] -
the brackets decide the typeSquare gives a list, `{k: v}` a dict, `{v}` a set, round a **generator** - not a tuple.
bash Example session python3 ~/py/comptypes.pylist [0, 1, 2, 3] listdict {0: 0, 1: 1, 2: 4, 3: 9}set {0, 1, 2}generator <generator object <genexpr> at 0x77ce1fd12810>tuple via (0, 1, 2, 3) -
the comprehension variableDoes not leak - NameError afterwards. A plain `for` loop does leave it bound.
bash Example session python3 ~/py/compscope.pyresult [0, 1, 2]Traceback (most recent call last): File "/home/sysadmin/py/compscope.py", line 3, in <module> print(x) ^NameError: name 'x' is not defined[exit 1] -
map(f, xs)Returns a lazy iterator in Python 3, not a list. Several iterables stop at the shortest.
bash Example session python3 ~/py/mapdemo.pymap returns <map object at 0x7b8708dbbbc0>as a list [0, 1, 4, 9, 16]with a builtin ['1', '2', '3']two iterables [11, 22]unequal lengths [11, 22]a comprehension [0, 1, 4, 9, 16] -
filter(None, xs)Keeps the truthy items. On the syllabus and almost never taught.
bash Example session python3 ~/py/filterdemo.pyfilter returns <filter object at 0x7b316fb77340>as a list [1, 3, 5, 7, 9]filter(None, xs) [1, 'a', [0]]a comprehension [1, 3, 5, 7, 9] -
reduceNameError - it lives in functools in Python 3, not the built-ins.
bash Example session python3 ~/py/reducenoimport.pyTraceback (most recent call last): File "/home/sysadmin/py/reducenoimport.py", line 1, in <module> print(reduce(lambda a, b: a + b, [1, 2])) ^^^^^^NameError: name 'reduce' is not defined[exit 1] -
[lambda: i for i in range(3)]All three return 2. They closed over the variable, not its value. `lambda i=i: i` fixes it.
One of the hardest questions in section 5.
bash Example session python3 ~/py/latebinding.pybuilt in a loop [2, 2, 2]with a default value [0, 1, 2] -
f.__closure__None for a plain function; a tuple of cells for a closure. `co_freevars` names what was captured.
bash Example session python3 ~/py/closurecell.py__closure__ Truehow many cells 1what is in them [5]the free variable ('n',)a plain function has None -
r · w · a · r+ · w+ · a+ · bw truncates on open, before you write anything. a always writes at the end. r is the default.
bash Example session cd ~/py/files && python3 modes.pyafter w : ['first', 'second']after a : ['first', 'second', 'appended']after w again: ['only this'] -
with open(...) as fCloses on every path out, including when an exception propagates through.
bash Example session cd ~/py/files && python3 withclose.pyinside the block, closed = Falsethe exception was caught out hereand the file is closed = True -
the file positionA second read() returns an empty string - the position is at the end. f.seek(0) rewinds.
bash Example session cd ~/py/files && python3 readn.pyread(4) 'line'read(4) ' one'tell() 8after seek(0), read(8): 'line one'read() to the end: '\nline two\nline three\nline ...read() again : '' -
readlines() keeps the newlinesAnd the last line has none if the file does not end with one - `['a\n', 'b']`.
bash Example session cd ~/py/files && python3 newlines.pyas read line one repr 'line one\n'stripped 'line one'splitlines ['line one', 'line two', 'line three', 'line four']no newline on the last line? ['a\n', 'b'] -
bytearrayA mutable bytes. Assign a number, not a character. `bytearray(3)` is three zero bytes.
bash Example session cd ~/py/files && python3 bamutable.pythe object bytearray(b'abc') bytearray len 3data[0] 97 intafter data[0] = 90 -> bytearray(b'Zbc')after append(33) -> bytearray(b'Zbc!') [90, 98, 99, 33]as bytes -> b'Zbc!'as a str -> Zbc! -
f.readinto(buffer)Fills a bytearray you already own and returns the count. Needs a mutable buffer.
bash Example session cd ~/py/files && python3 readinto.pybefore bytearray(b'\x00\x00\x00\x00') [0, 0, 0, 0]readinto returned 4after bytearray(b'\x00\x01\xffA') [0, 1, 255, 65]
No command matches that search.