Simple answer: in modern python every data type is a class, so formally there is no difference between the two solutions you proposed. (Please remember to use new-style classes: classic classes are obsolete! See http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes)
Now the question should be: how do I organize an efficient data structure in python? There is no doubt that the very idea of organizing the cells as an array of
class Cell instances is way too inefficient. You will end up with a mess of pointers and non-contigous data organized like a complicated linked list. You have of course the ability of easily insert new cells in your list: but do you need this feature? On the contrary you will have non contiguous data storage, and you have to access every cell by different levels of indirection.
If you organize your data as a
numpy.ndarray then data is memory-contiguous, and accessing different cells is simply done striding through your memory block: space efficient (no memory wasted for pointers) and fast.
As pointed out by Ethan, OO concepts should be used, but at higher level, once an efficient low level data structure has been implemented, usually through
OO programming means binding data to the methods that operate on the data itself at higher level of abstraction. (An example: I implemented a FEM code in which the stiffness matrix was defined as a class with a method for sparse super-nodal cholesky factorization. The first implementation was in-core: when an out-of-core implementation was needed, this was obtained via inheritance and minimal adjustments to the underlining data storage. Almost 100% of the super-nodal cholesky code was reused.)
A last comment, but crucial: an efficient numerical procedure is the result of a smart mapping of an algorithm and a data structure to your target computing architecture. If you start with the wrong data structure, there is no way of recovering efficiency, without a complete rewrite.