It sounds like what you want is the read-append mode.
with open('file', 'r+b') as file:
data = f.read(1)[0]
data = (~data) % 256
file.seek(0)
file.write(bytes((data,)))
Opening mode is r+b. r is read, + means also enable writing, b means binary, so the result of reads is a bytes object instead of a string.
f.read(1)[0] reads the first byte of the file and extracts that values from the resulting bytes object as an integer (the indexed value of a bytes object is an integer)
~data is the negation of the returned byte. Since python doesn't have a real byte type, we use % 256 to wrap the result back into the 0-255 range.
file.seek(0) returns to the start of the file so the next write replaces the first byte. Seek takes an offset in bytes.
Lastly just write the data back to the file. bytes takes a sequence of integers in the 0-255 range so we give it the tuple (data,). Alternatively, we could use the integer's to_bytes method and skip wrapping like this:
with open('file', 'r+b') as file:
data = f.read(1)[0]
data = ~data
file.seek(0)
file.write(data.to_bytes(1, 'little', signed=True))
Read returns a bytes object which is an array of bytes. When you access one, it returns the value at each index as an integer. To convert an array of bytes to a single integer you can use int.from_bytes. Its arguments are the bytes object, a string 'big' or 'little' indicating endianness, and an optional keyword argument signed which tells whether to interpret the bytes as signed or not. To convert back you can use .to_bytes which is a method on an int object which takes three arguments: the number of bytes to convert to, plus the endianness and whether or not to interpret signed data.
So to work with more than one byte at a time, you can read a different number of bytes.
You can seek to any byte offset within the file at any time before reading or writing. You can also use file.tell() to find out how many bytes are in a file before trying to seek or read.