Functions and modules
A function puts a name on a piece of work: def checksum(data) once, then call it anywhere. A module is just a .py file of such functions, so your bench scripts can share helpers instead of copying them.
def and return
def names a function and lists its parameters. return hands a value back to the caller; a function without it returns None. The optional hints after the colons, such as data: bytes, document what goes in and comes out. Python doesn't enforce them, but editors and checkers use them.
def checksum(data: bytes) -> int: """8-bit sum of every byte, as many simple frames use.""" return sum(data) & 0xFFdef is_valid(frame: bytes) -> bool: return checksum(frame[:-1]) == frame[-1]if __name__ == "__main__": good = bytes([0xAA, 0x29, 0x09, 0x01, 0xDD]) print(checksum(good[:-1]), is_valid(good)) print(is_valid(good[:-1] + b"\x00"))
221 True False
A module is a file
Any .py file is a module. Put frames.py next to your script, and from frames import is_valid gives you its function. The standard library is a collection of modules like this: struct, csv, time and pathlib all arrive with a single import.
# bench.py: reuse the helpers instead of copying themfrom frames import is_validdef handle(frame: bytes) -> None: if not is_valid(frame): print("bad checksum:", frame.hex(" "))
Python runs a module once, the first time it is imported, and reuses it after that. Keep each module small and focused: one for frame helpers, another for talking to the serial port. Then a test can import the frame helpers without a board connected.