I naively assumed that assigning a value via ellipsis [...]
, e.g.
a = np.empty(N, dtype=np.object)
a[...] = 0.0
is basically a faster version of the following naive loop:
def slow_assign_1d(a, value):
for i in range(len(a)):
a[i] = value
However this seems not to be the case. Here is an example for different behavior:
>>> a=np.empty(2, dtype=np.object)
>>> a[...] = 0.0
>>> a[0] is a[1]
False
the object 0.0
seems to be cloned. Yet when I use the naive slow version:
>>> a=np.empty(2, dtype=np.object)
>>> slow_assign(a, 0.0)
>>> a[0] is a[1]
True
all elements are the "same".
Funnily enough, the desired behavior with ellipsis can be observed for example with a custom class:
>>> class A:
pass
>>> a[...]=A()
>>> a[0] is a[1]
True
Why do get floats this "special" treatment and is there a way for fast initialization with a float values without producing copies?
NB: np.full(...)
and a[:]
display the same behavior as a[...]
: the object 0.0
is cloned/its copies are created.
Edit: As @Till Hoffmann pointed out, the desired behavior for strings and integers is only the case for small integers (-5...255) and short strings (one char), because they come from a pool and there never more than one object of this kind.
>>> a[...] = 1 # or 'a'
>>> a[0] is a[1]
True
>>> a[...] = 1000 # or 'aa'
>>> a[0] is a[1]
False
It seems as if the "desired behavior" is only for types numpy cannot downcast to something, for example:
>>> class A(float): # can be downcasted to a float
>>> pass
>>> a[...]=A()
>>> a[0] is a[1]
False
Even more, a[0]
is no longer of type A
but of type float
.