itertools does not recognize numpy ints as valid inputs on Python 3.6

亡梦爱人 提交于 2019-12-06 17:55:40

问题


Take this code:

import itertools as it
import numpy as np
data = ['a','b','c','d']
dw = np.array([1, 3], dtype=np.int64)
print(list(it.islice(data,dw[0],dw[1],1)))

On Python 2.7 it prints ['b', 'c',] as expected.

On Python 3.6 it throws an exception:

ValueError: Stop argument for islice() must be None or an integer: 0 <= x <= sys.maxsize.

The same goes for np.int32, and other methods of the itertools package throw similar errors, e.g. when you use permutations you get TypeError: Expected int as r.

I couldn't find much on this apart from this numpy issue and related ones, but that one was closed 3 years ago implying it was solved.

And basic things like indexing with numpy ints data[dw[0]] or boolean comparisons like dw[0] == 1 work just fine.

Am I missing something? Could this be a Python 3 bug?


回答1:


a numpy.int64 is apparently not a subclass of int

a, b = dw[0], dw[1]

type(a)

numpy.int64

isinstance(a, int)

False

Numpy documentation

The documentation mentions this explicitly

Warning

The int_ type does not inherit from the int built-in under Python 3, because type int is no longer a fixed-width integer type.

Solution

print(list(it.islice(data, int(dw[0]) , int(dw[1]), 1)))

or numpy slicing

data[dw[0]:dw[1]:1]



回答2:


I'm not sure if it's a bug in Python 3 or not, but it looks like the behaviour has changed since 2.7. As the numpy issue you linked described, under py27, either numpy.int32 or numpy.int64 would appear to be a subclass of int (depending on whether you use a 32- or 64-bit build of Python); under py3, the types are no longer related (numpy has fixed-width numeric types, python's int is variable-width).

The implementation of itertools.islice requires its arguments to be objects of type PyLong (which is the Python API name for the Python int type). Specifically, it calls PyLong_AsSize_t, which converts a Python object into a C size_t value. This method seems to require that its argument is actually a Python int object, since it calls PyLong_Check. I think this method is broadly equivalent to Python's isinstance(obj, int), which explains the difference in behaviour between py2 and py3 here.

Normal list indexing uses another more tolerant method to coerce arguments into positive integer values, called PyNumber_AsSsize_t. This checks if its argument is an int, and, if not, falls back to trying to call its argument's __index__ method; as @MarkDickinson points out, numpy's numeric types implement this method, so everything works fine. Perhaps this would be a more intuitive thing for itertools.islice to do.




回答3:


This looks like a case for the __index__ magic method (which numpy's integers already implement). I suggest raising an issue on the tracker, requesting this as an enhancement - that islice accept any object that implements __index__.




回答4:


If you want to keep a islice/slice-like object, use np.s_:

slice = np.s_[dw[0]: dw[1]: 1]
data[slice]

['b', 'c']

since np.s_ is a numpy object, it doesn't mind the numpy integers.



来源:https://stackoverflow.com/questions/44302946/itertools-does-not-recognize-numpy-ints-as-valid-inputs-on-python-3-6

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!