Q:

"Correct" way to interact with numpy objects

I've been using scientific python (via from pylab import *) on and off for a while as a free Matlab substitute, mainly for work in chemical engineering (I'm a fan of the IPython Notebook). Something that's always appeared strange to me is the fact that there's generally two different ways to interact with an object. For instance, if I have an array, I can tell its dimensions in two ways:
a = array([1,2,3],[2,3,4])
There's the 'Matlab' way:
shape(a)
Or instead I could find it by typing:
a.shape
This seems to contradict The Zen of Python: "There should be one-- and preferably only one --obvious way to do it"
I'm just wondering why there's multiple ways of doing the same thing, and which practice is more fundamental/natural for the language and would be better to use in the long run.

A:

Using the method is preferable.  After all, the implementation of shape simply defers to the method anyway (from /numpy/core/fromnumeric.py):
def shape(a):
    try:
        result = a.shape
    except AttributeError:
        result = asarray(a).shape
    return result

I assume a lot of this pylab stuff is just included to help ease the transition for people coming from MATLAB.  Get used to it because there are many more examples of numpy being, ahem, not very pythonic.  
When you get more used to python and matplotlib you will likely want to ditch the from pylab import * anyway and start writing more numpythonic code, rather than MATLAB style work.

A:

It mostly comes down to a matter of preference, but there are a few differences that you might want to be aware of. First off, you should be using numpy.shape(a) or np.shape(a) instead of shape(a), that's because "Namespaces are one honking great idea -- let's do more of those!" But really, numpy has several names you'll likely find in other python modules, ie array appears as array.array in python stdlib, numpy.array and numpy.ma.array, so to avoid confusing other (and yourself) just go ahead and avoid import the entire numpy namespace.
Other than that, turns out that numpy.shape, and most other similar functions, just look for a shape attribute/method on the argument and if they don't find one, they try and convert the argument to an array. Here is the code:
def shape(a):
    try:
        result = a.shape
    except AttributeError:
        result = asarray(a).shape
    return result

This can be useful if you want the shape of an "array_like" object, you'll notice that most numpy functions take "array_like" arguments. But it can be slow if you're doing something like:
shape = np.shape(list_of_lists)
mx = np.max(list_of_lists)
mn = np.min(list_of_lists)

Other than that, they're pretty much the same.

