Suppose I have a simple Test
class which inherits from numpy.ndarray
(documentation on ndarray
subclassing). This class creates an object which is a partial view of another ndarray
. Now I would like to create a method that modifies this view in-place. Something like:
import numpy
class Test(numpy.ndarray):
def __new__(cls, info='Test class'):
base = numpy.ndarray.__new__(cls, (6, 2), dtype=float)
view = base[0:2, :]
view.info = info
return view
def __array_finalize__(self, obj):
if obj is None:
return
self.info = getattr(obj, 'info', None)
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
self = self.base[start:stop]
if __name__ == '__main__':
t = Test()
print(t)
print(t.info)
t.change_view(1, 5)
print(t)
print(t.info)
I would expect the method .change_view(x)
to modify the view of the .base
attribute to show rows [1:5]
instead of [0:2]
, which is the default in __new__()
.
Is this possible? If so, how? Otherwise, why not? Note that .base
never changes (it always occupies the same memory space), I just want to change the view of it.
change_view()
. I know self = self.base[start:stop]
is not correct, and I know and understand why, but I think it is a short way to represent what I am looking for.self[:] = ...
or self.data = ...
. However, this will not work when changing the view size (which is exactly the case).This question was already asked here (or at least it is a similar case), but answers referred to solutions which returned the modified array (i.e. no in-place modification) or which used a wrapper object (i.e. having an attribute with the array that is to be modified).
I am aware of those possibilities, and I know they can be simpler or even more convenient. However, I would like to understand why (if so) it is not possible to do what I am looking for.
1) You can't change what data an ndarray instance or subclass instance points to in-place. Sorry! It turns out that it is possible to change which data an ndarray instance points to in-place, but it's a terrible idea and will hopefully be removed soon :-). See https://github.com/numpy/numpy/issues/7093
2) Your attempt to assign to self
like that will only change the binding of the self
variable inside the method's local binding, and I'm afraid this suggests a fairly fundamental misunderstanding of how Python's execution and object model work. I only bring this up because successfully subclassing ndarray
is hard. Really hard. Verging on impossible. (The developers of the popular pandas
library gave up, for example.) Despite the misleading impression given by that documentation, numpy is not really designed to support subclassing, and it does not work very well. I'd generally recommend that nobody at all attempt to subclass ndarray
; if you're not already an expert Python programmer than this goes tenfold. Probably we would have deleted that stupid page and disabled subclassing altogether except that it would break backwards compatibility :-(. I'd suggest finding a different strategy to approach your problem.
When you write code like
var = value
it assigns new value to var
and drops old value, however this is true only in current scope and all other aliases to previous value of var
stay unchanged. Python's self
is nothing special, it's just a variable, hence when you write
self = self.base[start:stop]
you just assign value of self.base[start:stop]
to the local variable self
but the value previsouly contained in self
was not changed as well as all other references that value (which is an instance of your Test class that you want to mutate). You can test it with the following implementation of change_view
:
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
print('before assignment')
print(self)
self = self.base[start:stop]
print('after assignment')
print(self)
print('end change_view')
I think running your code with this implementation should clear things up a bit. Possible implementation might be similar to this:
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
self[:] = self.base[start:stop]
The important difference is that it mutates the value contained in the variable self
instead of assigning new value to the variable. I think something like this should work with python lists, but numpy.ndarray
is more complex. Moreover since it's implemented in C
it might be impossible at all. For example you'll need to resize the view somehow, but you can't use numpy.ndarray.resize
because views don't own it's data; simply assigning size
and shape
won't work too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With