5

Is there something internal in python that treats arguments passed to __getitem_ _ differently, and automatically converts start:stop:step constructs into slices?

Here's a demonstration of what i mean

class ExampleClass(object):

  def __getitem__(self, *args):
    return args

  def __call__(self, *args):
    return args

  def randomMethod(self, *args):
    return args


a = ExampleClass()

#this works
print a[3:7:2, 1:11:2]

#syntax error on the first colon
print a.randomMethod(3:7:2, 1:11:2)
print a(3:7:2, 1:11:2)

#these work
print a.randomMethod(slice(3,7,2), slice(1,11,2))
print a(slice(3,7,2), slice(1,11,2))

Is it simply that the interpreter searches for instances of start:stop:step inside [], and swaps them out for slice(start, stop, step)? The documentation simply says:

The bracket (subscript) notation uses slice objects internally

Is this one of the python internal bits that i can't alter the behaviour of? Is it possible to make other functions take slice objects usign the start:stop:step shorthand?*

*I've seen the other question, Can python's slice notation be used outside of brackets?, but that just does it using a custom class, which i could easily do. What i want is a way to just use start:stop:step without having to wrap it in anything else.

SIDE NOTE:

It also apears that all arguments inside [...] are packaged up into a tuple, somewhat as if it were doing [*args] -> __getitem__(args).

class ExampleClass2(object):

  def __getitem__(self, arg):
    return arg

  def __call__(self, arg):
    return arg


b = ExampleClass2()

print b["argument 1", 2:4:6,3] # ('argument 1', slice(2, 4, 6), 3)
print b(slice(3,7,2), slice(1,11,2)) # TypeError: __call__() takes exactly 2 arguments (3 given)
3
  • 1
    fascinating! maybe a.randomMethod([3:7:2], [1:11:2]) or even a.randomMethod([3:7:2, 1:11:2]) would work... though according to zen of python I'd probably prefer to explicitly pass slice objects slice(1,11,2) into the method rather than exploiting some weird overloaded syntax Commented Dec 1, 2014 at 13:16
  • Not an answer but apparently Python converts [expression:expression:expression] into __getitem__(slice(...)) and only inside []. Commented Dec 1, 2014 at 13:19
  • @Anentropic I'm not looking to actually use any horrible weird syntax, i just like to understand weirdities of languages. python is nice in that regard as it initially seems like it hides them all from you, which makes them more surprising when you see one. Commented Dec 1, 2014 at 13:39

2 Answers 2

5

The Python grammar defines when you can use the slice operator:

trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
subscriptlist: subscript (',' subscript)* [',']
subscript: test | [test] ':' [test] [sliceop]
sliceop: ':' [test]

test is pretty much any expression, but it is only inside a subscriptlist that you can use the slice operator. So yes, the square brackets when used for subscripting are what matter, but square brackets used for lists won't magically allow you to write a slice, nor can you put a slice inside an arbitrary expression that just happens to be inside a subscript.

If you want slices when you aren't subscripting something you'll have to write slice(a,b,c).

Sign up to request clarification or add additional context in comments.

3 Comments

That's a shame. This grammar file looks like an enourmous horrible regex.
@will it's not a regex, it is BNF (en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form). The parser generator converts it into C source so you can be sure the human 'readable' version is also what Python understands.
I know it's not regex, 'twas just an analogy. Couldn't be a regex anyway, as it's not regular.
2

np.lib.index_tricks contains several 'functions' that accept this :: inputs, e.g. np.mgrid, np.r_, np.s_.

They are actually implemented as instances of classes, with __getitem__ definitions. And they are 'called' with brackets.

np.s_[2::2] #  slice(2, None, 2)
np.r_[-1:1:6j, [0]*3, 5, 6]  # array([-1. , -0.6, -0.2,  0.2, ... 6. ])
mgrid[0:5,0:5]

I don't normally use them, but they are an interesting example of how __getitem__ can be exploited.

np.insert is an example of a function that generates indexing tuples that include slices. np.apply_along also:

i = zeros(nd, 'O')
...
i[axis] = slice(None, None)
...
i.put(indlist, ind)
...arr[tuple(i.tolist())]

http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html has a relevant note:

Remember that a slicing tuple can always be constructed as obj and used in the x[obj] notation. Slice objects can be used in the construction in place of the [start:stop:step] notation. For example, x[1:10:5,::-1] can also be implemented as obj = (slice(1,10,5), slice(None,None,-1)); x[obj] . This can be useful for constructing generic code that works on arrays of arbitrary dimension.

2 Comments

This is pretty interesting. I'm not entirely sure why they decided to do it this way instead of just creating an a function that takes up to three arguments though, can you think of a reason why they chose to do it this way? The only reason i can think of is so that you can specify ranges, splitting dimensions with commas, but the same could be achieved by just passing tuples.
I think these functions date from a time when MATLAB had a stronger influence on how numpy developers thought.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.