6

[Python 3]

I like ndarray but I find it annoying to use.

Here's one problem I face. I want to write class Array that will inherit much of the functionality of ndarray, but has only one way to be instantiated: as a zero-filled array of a certain size. I was hoping to write:

class Array(numpy.ndarray):
  def __init__(size):
    # What do here?

I'd like to call super().__init__ with some parameters to create a zero-filled array, but it won't work since ndarray uses a global function numpy.zeros (rather than a constructor) to create a zero-filled array.

Questions:

  1. Why does ndarray use global (module) functions instead of constructors in many cases? It is a big annoyance if I'm trying to reuse them in an object-oriented setting.

  2. What's the best way to define class Array that I need? Should I just manually populate ndarray with zeroes, or is there any way to reuse the zeros function?

3
  • 3
    I don't see why you want to create your own class at all. What is the advantage over the numpy.zeros() factory function? If you don't like the name, just rename it, like create_array = numpy.zeros. Commented Jan 21, 2011 at 15:17
  • I am no longer sure there is an advantage. Maybe I am just not used to factory function. I'll have to think about it. Commented Jan 21, 2011 at 19:54
  • @Sven Marnach: I found several links on this general topic: stackoverflow.com/questions/628950/… stackoverflow.com/questions/2959871/… stackoverflow.com/questions/4617311/… Nothing specific to Python, but general comments from other languages seem to apply. And as far as I can tell, there's really no disadvantage to factory functions (other than my personal preference). Commented Jan 21, 2011 at 20:05

3 Answers 3

9

Why does ndarray use global (module) functions instead of constructors in many cases?

  1. To be compatible/similar to Matlab, where functions like zeros or ones originally came from.
  2. Global factory functions are quick to write and easy to understand. What should the semantics of a constructor be, e.g. how would you express a simple zeros or empty or ones with one single constructor? In fact, such factory functions are quite common, also in other programming languages.

What's the best way to define class Array that I need?

import numpy

class Array(numpy.ndarray):
    def __new__(cls, size):
        result = numpy.ndarray.__new__(Array, size)
        result.fill(0)
        return result
arr = Array(5)

def test(a):
    print type(a), a

test(arr)
test(arr[2:4])
test(arr.view(int))

arr[2:4] = 5.5

test(arr)
test(arr[2:4])
test(arr.view(int))

Note that this is Python 2, but it would require only small modifications to work with Python 3.

Sign up to request clarification or add additional context in comments.

9 Comments

Some remarks: 1. The shape parameter to the ndarray constructor may simply be an integer instead of a tuple. 2. dtype=float is the default. 3. In Python 3, you can omit the arguments to super() in this case. Combining these remarks, the first two lines of the constructor reduce to super().__init__(size).
Oh, and I just notice that your super() call is wrong -- should be super(Array, self) in Python 2.x.
You're right, I've edited my answer. (The constructor signature is undocumented, as usual.)
@Philipp: It's documented in the docstring of the class, not of the constructor itself. But this is actually where the doc belongs.
@Sven: What I mean is that the doc says shape has to be a tuple, and doesn't say dtype defaults to float.
|
7

If you don't like ndarray interface then don't inherit it. You can define your own interface and delegate the rest to ndarray and numpy.

import functools
import numpy as np


class Array(object):

    def __init__(self, size):
        self._array = np.zeros(size)

    def __getattr__(self, attr):
        try: return getattr(self._array, attr)
        except AttributeError:
            # extend interface to all functions from numpy
            f = getattr(np, attr, None)
            if hasattr(f, '__call__'):
                return functools.partial(f, self._array)
            else:
                raise AttributeError(attr)

    def allzero(self):
        return np.allclose(self._array, 0)


a = Array(10)
# ndarray doesn't have 'sometrue()' that is the same as 'any()' that it has.
assert a.sometrue() == a.any() == False
assert a.allzero()

try: a.non_existent
except AttributeError:
    pass
else:
    assert 0

6 Comments

Very neat. I assume that all numpy global functions have the same semantics when called with ndarray argument as the same-name instance method of ndarray.
To change to Python 3, I just remove (object) from the class definition, correct? [Not that leaving it would cause problems.]
@max: the code works without changes on both 2.x and Python 3.
I seem to be missing something here. You don't derive from ndarray, but if any attribute is requested, you first try to get the attribute from the ndarray instance. Wouldn't it be much easier to derive from ndarray in the first place? You still can add the magic to add the module level functions in numpy as methods, but I don't think this is a good idea either, because most of them are already methods, and the rest does not make sense as a method (with very few exceptions).
@Sven Marnach: The premise is that the OP finds ndarray annoying to use. Delegation is a more flexible approach to change class interface than inheritance. The code is not a final solution but it is just a mere skeleton example that allows to extend it to use black-, white- list approaches, etc. I agree about module level functions. The code shows how you can do it; it doesn't advocate that it is a sensible thing to do.
|
4

Inheritance of ndarray is little bit tricky. ndarray does not even have method __init(self, )___, so it can't be called from subclass, but there are reasons for that. Please see numpy documentation of subclassing.

By the way could you be more specific of your particular needs? It's still quite easy to cook up a class (utilizing ndarray) for your own needs, but a subclass of ndarray to pass all the numpy machinery is quite different issue.

It seems that I can't comment my own post, odd
@Philipp: It will be called by Python, but not by numpy. There are three ways to instantiate ndarray, and the guidelines how to handle all cases is given on that doc.

3 Comments

I seem to have missed this document. But why is the __init__ method in my example called even though ndarray had __new__?
@Philipp: see my comment to your answer.
I just wanted my own class that I fully control how it's instantiated. You're right subclassing ndarray is too hard. I'll follow the container approach in @J.F. Sebastian.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.