0

I created a numpy array and tried to get its attributes with 'dict', but failed.

>>> import numpy as np
>>> a = np.arange(12)
>>> a.reshape(3,4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> type(a)
<class 'numpy.ndarray'>
>>> a.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'numpy.ndarray' object has no attribute __dict__. Did you mean: '__dir__'?
>>> '__dict__' in dir(a)
False

The document says that obj.dict is used to store an object's (writable) attributes. I know I can use dir(a) and getattr() to fetch all the attributes {k,v}. Also, all the attributes such as shape and itemsize can be derived from the array constructor and besides strides can be calculated like below:

def calStrides(self, shape, itemSize):
    loops = len(shape)
    print(f"calculating strides... loop times = {loops}")
    lst = []
    for i in range(loops):
        y = 1   #   first multiplicand
        for j in range(i + 1, loops):
            y *= shape[j]
        lst.append(y * itemSize)
    self.cal_strides = tuple(lst)

Does this make them 'read-only' (as opposed to assignment or 'writable')? Is that the reason why there is no dict provided for data inspection?

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
Leon Chang
  • 669
  • 8
  • 12
  • 1
    `ndarray` is created by compiled code, `__new__` and `__init__`, so doesn't automatically have a `dict`. Even a user defined class can have `slots` instead of a dict. `arr.dtype`, `arr.shape` and `arr.strides` are basic documented attributes. They may be implemented as `properties`, but again in compiled code. – hpaulj Mar 15 '23 at 00:28
  • 1
    No, it means that it doesn't use an attribute dict, so it generally won't support *arbitrary attributes*. By default, user-defined classes have a `__dict__` (although, you can define a `__slots__` to prevent that). So just consider `class Foo: pass` then` foo = Foo()`, now I can do `foo.x = 42` or `foo.banana = 'bar'`, notice, I can just create and assign to any attribute I want. – juanpa.arrivillaga Mar 15 '23 at 03:57
  • The important thing to understand is that `.__dict__` **is the instance namespace in those cases**. It isn't *collecting the attributes into a dict*, the dict itself functions as teh namespace. So if I do `foo = Foo(); d = foo.__dict__; d['bar'] = 42; print(foo.bar)` I get `42` – juanpa.arrivillaga Mar 15 '23 at 03:58
  • 1
    Note, not having a `__dict__` isn't really abnormal, most built-in types don't have a `__dict__`! That's why I cannot do: `x = 1; x.foo = 'bar'`. – juanpa.arrivillaga Mar 15 '23 at 03:59
  • Indeed, ``type(x) x.__dict__ 'int'`` shows: ``AttributeError: object has no attribute`` just like ndarray's case. But you can use `dict((k, getattr(x, k)) for k in dir(x) if (k[0]!='_' and (repr(getattr(x,k))[0] != '<')))` to get ``{'denominator': 1, 'imag': 0, 'numerator': 1, 'real': 1}`` – Leon Chang Mar 16 '23 at 01:41

1 Answers1

1

With a sample array in my current ipython session:

In [157]: x
Out[157]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

We can easily ask for attributes like:

In [158]: x.shape,x.dtype,x.strides
Out[158]: ((2, 3, 4), dtype('int32'), (48, 16, 4))

A more comprehensive description of the array is available with:

In [159]: x.__array_interface__
Out[159]: 
{'data': (2046444311360, False),
 'strides': None,
 'descr': [('', '<i4')],
 'typestr': '<i4',
 'shape': (2, 3, 4),
 'version': 3}

As you note it does not have a dict:

In [160]: x.__dict__
AttributeError: 'numpy.ndarray' object has no attribute '__dict__'

It's created with compiled numpy code, not with a class ndarray():... expression.

strides can be calculated as you show, if the array is 'C' contiguous, but you don't need to do that work. Plus strides can be changed, as with the transpose:

In [161]: x.T.strides
Out[161]: (4, 16, 48)

This particular array was create with a np.arange(24).reshape(2,3,4), so it has a base; it's a view:

In [162]: x.base
Out[162]: 
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])

shape etc can, within limits be modified in-place, but usually I use functions or methods like x.reshape or x.astype(float). Those return a new array, which may be a view or a copy.

While x doesn't have a __dict__, I can still get a list of its attributes, including methods:

In [175]: [i for i in dir(x) if i.startswith('s')]
Out[175]: 
['searchsorted',
 'setfield',
 'setflags',
 'shape',
 'size',
 'sort',
 'squeeze',
 'std',
 'strides',
 'sum',
 'swapaxes']

Thus I can look at its size and nbytes,

In [177]: getattr(x,'size')
Out[177]: 24

In [178]: getattr(x,'nbytes')
Out[178]: 96                     # size*itemsize
hpaulj
  • 221,503
  • 14
  • 230
  • 353