Re: More lazyness than anything
What you describe as being difficult with handing bytes is the situation in version 2. Strings could be 8 bit ASCII bytes or they could be unicode, depending on what any particular function decided to return, which in turn could depend on your OS locale settings, the phase of the moon, or any number of other factors. A "string" of bytes could change into a string of unicode unexpectedly if you weren't careful.
In version 3, strings were made to be purely unicode all the time, and new "bytes" and "bytearray" types were added which were purely intended as arrays of raw bytes and not to be interpreted as unicode strings.
There was also however added a full set of byte formatting and manipulation functions added which let you do string-like operations on bytes and bytearrays (searching, replacing, justifying, padding, ASCII case changes, formatting, etc.). So now you can write out bytes to image files and the like an be sure that what you are getting is bytes.
So now with version 3 strings are text and are unicode all the type, while bytes are bytes and not to be confused with text strings.
This change ironically is what the biggest complaints were about, mainly originating with people who only ever used 8 bit American ASCII and were not happy about having to change their code to better accommodate people who wanted to use funny looking accented characters as part of their alphabet.
In version 2 I was initially very uncomfortable using the "struct" module in the standard library (used to format packed binary data - very useful, have a look at it) because of it's use of strings when it wasn't clear when a string was an array of bytes or when it was a sequence of unicode characters. With version 3 it uses bytes and bytearrays and there can be no confusion with inadvertent unicode.