Q:

Python: Syntax appears to be exponentiating an array? Help me interpret?

I'm trying to decipher some complicated code. Below, I've simplified the code, of what I do understand of it, to get to the heart of my question. 
scales = (2**arange(8, 12, .25)).astype(int)

It seems to me that arange() creates an array of values, ranging from 8 to 11, with values 0.25 apart. 
But then what does 2**arange do? I know ** is for exponentiation, but it doesn't make sense to me that one could exponentiate an array! Exponentiating the values inside the array make sense, sure. But that seems like very strange syntax for it!

A:

Numpy arrays let you apply numeric operators to all elements in the array. So array * 3 would apply the multiplication to all elements in the array, producing a new array with the results. You can use an array on either side of such an expression; after all, not all operators are commutative.
Using 2 ** array simply applies each element in the array as an exponent of 2, producing an array with the 2 ** <input item> calculation:
>>> arange(8, 12, .25)
array([  8.  ,   8.25,   8.5 ,   8.75,   9.  ,   9.25,   9.5 ,   9.75,
        10.  ,  10.25,  10.5 ,  10.75,  11.  ,  11.25,  11.5 ,  11.75])
>>> 2**arange(8, 12, .25)
array([  256.        ,   304.43702144,   362.03867197,   430.53896461,
         512.        ,   608.87404288,   724.07734394,   861.07792922,
        1024.        ,  1217.74808576,  1448.15468787,  1722.15585844,
        2048.        ,  2435.49617153,  2896.30937574,  3444.31171688])

So the input is an array with 8, 8.25, 8.5, etc., and the resulting array contains the result of 2 ** 8, 2 ** 8.25, 2 ** 8.5, and so on.
The array.astype(int) operation then floors the results:
>>> (2 ** arange(8, 12, .25)).astype(int)
array([ 256,  304,  362,  430,  512,  608,  724,  861, 1024, 1217, 1448,
       1722, 2048, 2435, 2896, 3444])

