If any kind of character gets loaded into a string, what do I use for an end-of-line character?
Framing binary messages is not as simple as choosing a delimiter.
Counting the characters could be an option, but you need some way to
check that your reception has not gotten out of sync. You could for
example send messages that looks like:

struct Message {
    uint32_t magic;
    uint8_t data[MEM_LEN];
    uint16_t crc;
};

Where magic in a magic number, i.e. a random but fixed value that
is very unlikely to happen by accident, and you end the message with a
CRC. On the Python side, you check that both the magic number and
the CRC are OK. If they are not, you have a communication error, but you
can re-synchronize by looking for the next occurrence of the magic
number.
Another option is to choose both a delimiter (say, ASCII 10 = LF =
'\n') and an escape character (say, ASCII 92 = backslash = '\\'). On
the sending side, you prefix by the escape character every accidental
occurrence of either the escape character or the delimiter:
void send_byte(uint8_t c)
{
    if (c == '\\' || c == '\n')
        Serial.write('\\');  // prefix by escape character
    Serial.write(c);
}

and on the Python side you do the reverse operation. The only issue with
this approach is that your messages are not constant size anymore.