Difference between setting an attribute and calling a mutation method in place?

I mainly use numpy to perform data analysis without understanding the underlying program, so this may be obvious.

I do not understand the difference between setting an attribute, simply assigning it against a method call that modifies this attribute in place. An example where you can do this:

In [1]: import numpy as np In [2]: a = np.array([[1, 2, 3], ...: [4, 5, 6]]) In [3]: a.shape Out[3]: (2, 3) In [4]: a.reshape(3,2) Out[4]: array([[1, 2], [3, 4], [5, 6]]) In [5]: a Out[5]: array([[1, 2, 3], [4, 5, 6]]) In [6]: a.resize(3,2) In [7]: a Out[7]: array([[1, 2], [3, 4], [5, 6]]) In [8]: a.shape = (6,) In [9]: a Out[9]: array([1, 2, 3, 4, 5, 6]) In [10]: a.__setattr__('shape',(3,2)) In [11]: a Out[11]: array([[1, 2], [3, 4], [5, 6]]) 

I do not understand what is the difference between inputs 6 and 8 . It is clear that both modify the a.shape attribute in place, rather than returning the reconstructed object, as in 4 . Both of them only call a.__setattr__() , as in 10 ? If so, why do they both exist?

(I know that a.resize() has additional capacity to increase or decrease the allocated memory, but I do not use it here --- does this duality only using the method adds some other capacity?)

+4
source share
2 answers

I read your question several times and think that I can solve everything:

 a.reshape(3,2) # returns a new array a.resize(3,2) # returns None, operates on the same array # (equivalent to modifying ndarray.shape here since no. of elements is unchanged) a.shape = (6,) # operates on the same array, uses property trick to hide detailed # setter logic behind normal attribute access. # note the parens are unnecessary here - equivalent is a.shape = 6, # NOT equivalent to a = a.reshape(6,), which would change the id(a) a.__setattr__('shape',(3,2)) # just an ugly way of doing a.shape = 3,2 

Your main question, apparently, concerns the non-uniqueness of methods for changing the shape of an array.

It is clear that both change the a.shape attribute in place, rather than returning the reconstructed object, as in 4

Yes. Or, more precisely, both change the array in place (and the value returned by the shape attribute as a result) changes.

They both only call a.__setattr__() , as in 10?

It is not necessary that they call a.__setattr__(...) . Instead, they can change some internal variable self._shape , and it can still change what a.shape returns.

You can see who really uses __setattr__ by creating his own class, for example:

 class my_ndarray(np.ndarray): def __setattr__(self, name, value): print '__setattr__ called with name={}, value={}'.format(name, value) super(my_ndarray, self).__setattr__(name, value) a_ = my_ndarray(a.shape) a_[:] = a 

In this case, the answer is that neither a_.resize nor a_.reshape uses __setattr__ .

If so, why do they both exist?

The fact that resize can do much more than redo when the number of elements is different is reason enough for me. It would be weird to use resizing when all you had to do was change shape, but why numpy (which should be high-performance) is worried about warning you or artificially forbid you to use resizing when you β€œcan” change form instead?

If you are concerned about the obvious violation of zen python # 13, numpy not the best place to look for consistencies. Just compare np.eye and np.identity , for example! What is numpythonic is not always pythonic.

+1
source

The example in 8 is actually called property , which gives you access to versions 2.1+.

eg.

 @property def shape(self): """I'm the 'shape' property.""" return self._shape @shape.setter def shape(self, value): self._shape = value 

__setattr__ calls a setter :

 x.__setattr__('shape', value) 

equivalent (see setter property above).

 x.shape = value 

The core logic always calls the modifier function.

+2
source

All Articles