The getitem operator
This prototypes __getitem__ using the Python interface, introducing any fancy features we'd need to pull off that. If that won't work, we'll roll our own __cgetitem__ with an easier compilable interface, at least for now.
How Python getitem works
1 >>> class A: 2 ... def __getitem__(self, idx): print repr(idx) 3 ... 4 >>> a = A() 5 >>> a 6 2 7 >>> a[2,3] 8 (2, 3) 9 >>> a[...,4] 10 (Ellipsis, 4) 11 >>> a[1:2] 12 slice(1, 2, None) 13 >>> a[1:2:-1, ..., 4] 14 (slice(1, 2, -1), Ellipsis, 4)
- ECTO - "Easily Compile-Time Optimizeable"; provided that the function is inlined.
- CTO - Possibly compile-time optimizeable with a not-too-complex visitor simulating a Python interpreter for compile-time-known expressions and statements. I.e., this comment below means that the values involved will be known at compile-time in the regular use-case (and that not optimizing is the right thing to do if it is not known)
- DBA - "Disappears Because of Assumptions".
I'll treat self.data always as the underlying C buffer rather than the Python attribute -- this issue is somewhat orthogonal, and anyway I'd guess that  would lead to the C "overload" getting selected.
This is inside an cdef class ndarray block in a pxd file.
1 # Use "generic" (parameter polymorphism) in order to avoid certain typing issues 2 # that will be explained as we go. "index" will have to be compile-time optimized 3 # anyway, so can as well make it object. 4 # 5 # "self" is generic because in my parameter polymorphism draft, I propose that 6 # assumptions gets carried over when "generic" is used. I.e., using "generic" 7 # for self makes sure that one instance of the method is created per 8 # assumption-combination. (This is, I suppose, only really useful if the method 9 # is not inlined though. So might make it "object" and rely on inlining optimization 10 # instead *shrug*) 11 12 cdef generic __getitem__(generic self, object index): 13 if isinstance(index, int): # ECTO 14 if not self.ndim == 1: # DBA 15 return (<object>self)[index] # gets a slice 16 else: 17 # Somehow use an assumption, self.dtype, in a type context. If the dtype assumption 18 # is not made then this will raise compile-time error. 19 # Since the user is "probably" assigning this to a variable of the right type, 20 # the "generic" return value will be the correct one (or if not, the return statement 21 # will turn into raising the correct coercion error) 22 return (<self.dtype*>(self.data + self.strides * index)) 23 else: # ECTO, is a tuple 24 # All of the below is CTO, but definitely the hardest part 25 offset = 0 26 for idx, item in enumerate(index): 27 if i.__class__ is slice or i is Ellipsis: break 28 else: offset += self.strides[idx] * item 29 else: 30 # can use direct access 31 return (<self.dtype*>(self.data + offset)) 32 # Encountered break, fall back to Python 33 return (<object>self)[index]