To help others with this topic, the way I have extracted the engineering values is as per the below class method:
def extract_value(self,data):
bit_list = bin(int.from_bytes(data,"little"))[2:]
packed_bit_str = zfill(bit_list,64)
spn_relevant_str = packed_bit_str[(-1*(self.start_bit+self.msg_bits)):(-1*(self.start_bit))]
self.engineering_value = ((int(spn_relevant_str, 2)) * self.scaling_factor) + self.offset
I instantiate the class for each SPN, and for each PGN/msg the method is ran for all SPN's where the CAN ID matches the PGN ID.
The reason for working in bits as opposed to bytes is that some messages dont take up a full byte or share bytes with other messages.
I am new to Python and am in no way an experienced programmer, but though I would share my method since this didnt seem to be a commonly discussed topic.