Repository navigation
BUG: np.str_ and np.bytes_ object have a confusing __repr__ and __str__ #15477
Copy link
Copy link
Open
Description
>>> s = np.str_("\0\0")
>>> list(s)
['\0', '\0']
>>> len(s)
2
>>> s
'' # wat>>> b = np.bytes_(2)
>>> list(b)
[0, 0]
>>> len(b)
2
>>> b
b'' # watCaused by
numpy/numpy/core/src/multiarray/scalartypes.c.src
Lines 389 to 425 in f15026c
| /* | |
| * over-ride repr and str of array-scalar strings and unicode to | |
| * remove NULL bytes and then call the corresponding functions | |
| * of string and unicode. | |
| */ | |
| /**begin repeat | |
| * #name = string*2,unicode*2# | |
| * #form = (repr,str)*2# | |
| * #Name = String*2,Unicode*2# | |
| * #NAME = STRING*2,UNICODE*2# | |
| * #extra = AndSize*2,,# | |
| * #type = npy_char*2, Py_UNICODE*2# | |
| */ | |
| static PyObject * | |
| @name@type_@form@(PyObject *self) | |
| { | |
| const @type@ *dptr, *ip; | |
| int len; | |
| PyObject *new; | |
| PyObject *ret; | |
| ip = dptr = Py@Name@_AS_@NAME@(self); | |
| len = Py@Name@_GET_SIZE(self); | |
| dptr += len-1; | |
| while(len > 0 && *dptr-- == 0) { | |
| len--; | |
| } | |
| new = Py@Name@_From@Name@@extra@(ip, len); | |
| if (new == NULL) { | |
| return PyUString_FromString(""); | |
| } | |
| ret = Py@Name@_Type.tp_@form@(new); | |
| Py_DECREF(new); | |
| return ret; | |
| } | |
| /**end repeat**/ |
It looks to me like we should just remove our overloads of these functions, they don't make any sense.
Reactions are currently unavailable
Activity
pv commentedon Jan 30, 2020
Is it like this to be consistent with the overall stripping of trailing NULs in S and U dtypes in arrays? It's not just string repr, but also
x = np.array([b'a\0\0\0']); assert type(x[0]) is np.bytes_; assert x[0] == b'a'and probably other things too.eric-wieser commentedon Jan 30, 2020
If that's the intent, we should be stripping the nulls upon construction, not upon printing. As a more obviously broken case, consider:
I think this only affects scalars constructed directly, not those extracted from arrays.
I'm not sure whether
np.bytes_(b'a\0') == b'a'is actually true, even though it looks like it is when you printpv commentedon Jan 30, 2020
Indeed,
len(x[0]) == 1, so item extraction seems to generate NUL-stripped string scalars. So maybe the reprs indeed don't make sense.seberg commentedon Jan 30, 2020
Yeah, the NULL stripping is always an issue we have, and something we cannot get around. I think the mental model of the NULLs being stripped when the NumPy scalar is created is the best thing we can probably do. We could think about adding a warning when null padded strings/bytes are put into a NumPy array, since they will not roundtrip correctly anyway...
eric-wieser commentedon Feb 1, 2020
So I think after removing these overloads, our options are to have the numpy subclasses:
.dtypeattribute (ie, do nothing else)NULLs on construction. Note this will cause things likelen(np.str_("a\0b")[:2]) == 1, which is perhaps surprisingNULLs on construction but warnNULLs are present, but keep themA variant of 4 would be to pick 1, and instead change
UNICODE_setitemto warn when trailing nulls are stripped, which might be more user-friendly, and would catch the behavior whether numpy scalars are used or not.seberg commentedon Feb 1, 2020
I am for Option 3.
Any option should have the same result as
np.array(string, dtype="S")[()]andnp.array(str, "S")andnp.string_(str)should be as similar as possible (at the moment at least, due toPyArray_Return).Since it can be a bug for someone who needs the zero bytes, I think a warning makes sense. The only question is whether the warning for some reason is prohibitively noisy. I do not expect that. It could point out that
voidcan sometimes be a way to spell bytes of fixed length.eric-wieser commentedon Feb 1, 2020
I'm leaning towards the option 4 variant. The more I think about it, 3 violates a lot of assumptions that being a subclass of
strwould imply.Consider:
If passed a
np.str_with your proposed changes, this will remove inner nulls from"a\0.ext"seberg commentedon Feb 1, 2020
True, although that code will probably give the warning (although if you have many of that you would not notice). Its true from a subclassing perspective...
It seems we either have to sacrifice consistent subclassing or consistency with arrays? Since this is NumPy scalar, which is almost an array, I think I still lean a bit towards 3. But basically there is no good solution and either 3. or 4. seem better than the current state to me.
Zac-HD commentedon Feb 2, 2020
I'm with @seberg - personally I'd choose consistency with arrays, but either option that emits a warning is reasonable.