It may help to distinguish between what the array stores in its data buffer, and what it returns via indexing.
a=np.arange(6,dtype=int).reshape(2,3)
Its attributes, a.__array_interface__
{'strides': None,
'descr': [('', '<i4')],
'shape': (2, 3),
'data': (171366744, False),
'version': 3,
'typestr': '<i4'}
The values are stored a 24 bytes (6 * 4), starting at the data memory location.
a[0,0] isn't just 4 bytes from the data buffer. It is really a single item 0d array. Technically its type is np.int32, but it has many of the same attributes and methods as an np.ndarray:
In [431]: a[0,0].__array_interface__
Out[431]:
{'strides': None,
'descr': [('', '<i4')],
'shape': (),
'__ref': array(0),
'data': (171093512, False),
'version': 3,
'typestr': '<i4'}
.item() method can be used to pull that value out of the array object. In many cases an int32 can be used just like a Python primitive int, but evidently your function is picky, performing some sort of isinstance(i,int) test.
type(a[0,0]) # numpy.int32
type(a[0,0].item()) # int
type(int(a[0,0]) # int
a.tolist()[0][0] because that array method is designed to construct a nested list of Python primitives, stripped of all ndarray attributes. In effect it does .item() at the lowest level. Effectively it is:
In [444]: [[j.item() for j in i] for i in a]
Out[444]: [[0, 1, 2], [3, 4, 5]]
int().int(a)fails.f(int(a[0,0])).