Issue
I know that tuple assignment in Python such as the following
a, b = b, a
works by first packing b
and a
into a tuple (b, a)
and then unpacking it to a
and b
, so as to achieve swapping two names in one statement. But I found that if a
and b
are replaced by sliced Numpy arrays:
# intended to swap the two halves of a Numpy array
>>> import numpy as np
>>> a = np.random.randn(4)
>>> a
array([-0.58566624, 1.42857044, 0.53284964, -0.67801528])
>>> a[:2], a[2:] = a[2:], a[:2]
>>> a
array([ 0.53284964, -0.67801528, 0.53284964, -0.67801528])
My guess is that the packed tuple (a[2:], a[:2])
is actually a tuple of "pointers" to the memory location of a[2]
and a[0]
. When unpacking, first a[:2]
is overwritten by values starting from the memory at a[2]
. Then a[2:]
is overwritten by values starting from a[0]
. Is this the correct understanding of the mechanism?
Solution
So, this isn't simple assignment. Name-object binding semantics apply to simple assignment, i.e. a = b
.
If you do:
a[ix] = b
Then the exact behavior is deferred to the type, i.e., it is equivalent to
type(a).__setitem__(a, ix, b)
Numpy arrays are basically object-oriented wrappers over primitive, C-like arrays, implementing a "true" multidimensional array interface. In this context, the key thing to understand is that different numpy array objects can share underlying buffers. Simple slicing always creates a numpy.ndarray object that is a view over the original array.
So in this case, the b
above is actually a call to nd.array.__getitem__
. Which returns a view.
So, consider the simple case of Python lists. The right hand side:
(a[2:], a[:2])
Creates a tuple of two, independent list objects (although, shallow-copied).
When they are assigned to the sequence of assignment targets on the left-hand side, the mutation doesn't have any shared effect. There are three independent buffers for the three list objects (list objects will not create views).
On the other hand, the expression a[2:], a[:2]
creates a tuple with the result of slicing the original nd.array object, controlled by nd.array.__getitem__
. This creates two new array objects, but they are views over the underlying buffer from the original array. Basically, you are sharing the same underlying mutable, primitive buffer between three different array objects.
Answered By - juanpa.arrivillaga
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.