Why is shape an attribute and a function but not a method of arrays?

arrays, numpy, python

Solution

As aruisdante notes, PEP 8 says:

For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods.

I think the actual convention in practice is even a bit more than that. If what you're getting from the object is just static data, there is no need to make it a method; it can just be an attribute. That is, if an object has data attached to it that is already calculated and stored, it can be stored as an attribute. Methods are more commonly used when retrieving the needed data requires some sort of actual computation to be done every time you retrieve it. (Properties allow for "transparent" computation even on simple attribute access, so that `obj.attr` actually runs a function to calculate the result, but using this for any heavy computation would be considered a bit sneaky.) Also, of course, if you need to pass arguments in order to get the data you want, it has to be a function/method (which is why `np.shape` is a function). There is certainly scope for differences of opinion here, and there are indeed libraries that vary in what they expose as attributes vs. methods.

So, since `shape` is a simple fixed feature of the array, it doesn't need to be a method.

Problem

Experienced R user, relatively new Python user. Will delete if the consensus is that this is too much of an opinion/mind-of-the-designer question. I'm really curious why shape is an attribute of arrays and a function in the numpy module but not a method of array objects. In other words, ``` import numpy as np a = np.array((1,2,3)) np.shape(a) ## call function ## (3,) a.shape ## retrieve attribute ## (3,) a.shape() ## pretend that it's a method ## Traceback (most recent call last): ## File "<stdin>", line 1, in <module> ## TypeError: 'tuple' object is not callable ``` It's fairly obvious, proximally, why the last approach doesn't work (because `a.shape` returns `(3,)` and then we are trying to compute `(3,)()`), but I don't understand the design -- very naively, I would have expected a shape method to be the most Pythonic.

Original source