I am trying to reference a slice of a "global" numpy array via a object attribute. Here is what I think the class structure would be like and it's use case.
import numpy
class X:
def __init__(self, parent):
self.parent = parent
self.pid = [0, 1, 2]
def __getattr__(self, name):
if name == 'values':
return self.parent.P[self.pid]
else:
raise AttributeError
class Node:
def __init__(self):
self.P = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self._values = X(self)
def __getattr__(self, name):
if name == 'x':
return self._values.values
else:
raise AttributeError
Here is the use case:
>>> n = Node()
>>> print n.P
[ 1 2 3 4 5 6 7 8 9 10]
>>> print n.x
[1 2 3]
>>> print n.x[1:3]
[2 3]
Which works fine, now I would like to assign values to n.P
through the n.x
attribute by,
>>> n.x = numpy.array([11, 12, 13])
to get,
>>> print n.P
[ 11 12 13 4 5 6 7 8 9 10]
Or assign values to slices by,
>>> n.x[1:3] = numpy.array([77, 88])
to get,
>>> print n.P
[ 11 77 88 4 5 6 7 8 9 10]
But for the life of me, I'm struggling to get this assignment working. I thought it would be easy using __setattr__
and __setitem__
, but a whole day later I still haven't managed it.
Ultimately, n.x
will be returned as a multi-dimensional array where the X
class will reshape is on return, but is stored in a n.P
which is a vector. I have removed this to simplify the problem.
I would love some help on this. Has anyone done this before? Or suggest how to do this?
Thanks in advance for your help.
SOLUTION
So after many days of stumbling around I found a solution. I suspect this can be simplified and refined. The solution is to create a X object in your Node object. When it's retrieved, it returns temporary numpy object (Values) with knowledge of it's parent node and pids. The setslice_ function is defined in this update the global P array with the new values. If the X object is is assigned, it doesn't return a Values object but sets the global P values directly.
Two points, which may be invalid: 1. The Node and X objects had to be a sub-class of object; 2. if setting a higher dimension array, you need to use __setitem__
instead, which won't work on 1D arrays or lists.
As I said I suspect this code can be improves since I'm not sure I fully understand it. I am happy to take improvements and suggestions.
Thanks for your help, especially Bago.
Here is my final code.
import numpy
class Values(numpy.ndarray):
def __new__(cls, input_array, node, pids):
obj = numpy.asarray(input_array).view(cls)
obj.node = node
obj.pids = pids
return obj
def __setslice__(self, i, j, values):
self.node._set_values(self.pids[i:j], values)
class X(object):
def __get__(self, instance, owner):
p = instance.P[instance.pids]
return Values(p, instance, instance.pids)
def __set__(self, instance, values):
instance.P[instance.pids] = values
class Node(object):
x = X()
def __init__(self, pids=[0, 1, 2]):
self.P = numpy.arange(11)
self.pids = pids
def _set_values(self, pids, values):
self.P[pids] = values
node = Node(pids=[4, 5, 6, 7])
print '\nInitial State:'
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]
print '\nSetting node.x = [44, 55, 66, 77]:'
node.x = [44, 55, 66, 77]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]
print '\nSetting node.x[1:3] = [100, 200]:'
node.x[1:3] = [100, 200]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]