Of course I'm unhappy with it, it doesn't behave the way I think it should, and it's not consistent.
Consistent with what? (Before you rush in an answer, remember that there are almost always multiple sides to a consistency argument.)
I don't see what's wrong with those. Both produce valid expressions that, when entered, compare equal to the object whose repr() was printed. What more would you *want*?
I find that the definition str is inconsistent indeed, because the items in a string are strings again, not characters (or code points). I don't think there is too many other examples in Python where the same is true; indexing a list does not give a list but the item that is at the point. In [4]: type(b'abc') Out[4]: builtins.bytes In [5]: type(b'abc'[1]) Out[5]: builtins.int In [6]: type('abc') Out[6]: builtins.str In [7]: type('abc'[1]) Out[7]: builtins.str there is no byte type in Python, so the closest is int (there is a byte type in numpy); if there was one, indexing a byte array could return that, but I assume the use case would be quite limited. But that there is no "characters" but only strings of length one is a confusing concept. It is as of scalars were the same as arrays of length one. These are different concepts, however. (Though, admittedly, numpy will take arrays of length 1 as scalars at least in some cases as a convenience - though I think it should not as it prevent users from writing consistent code that will be easy to read later. The same is here the case for Python with strings.) In [11]: [1,2,3] + [1] Out[11]: [1, 2, 3, 1] In [12]: [1,2,3] + [1][0] TypeError: can only concatenate list (not "int") to list In [13]: 'abc' + 'd' Out[13]: 'abcd' In [14]: 'abc' + 'd'[0] Out[14]: 'abcd' so, yes, the interface to strings and arrays is inconsistent. At least in this aspect.