Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python slice objects and __getitem__

Tags:

python

slice

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)
like image 461
will Avatar asked Dec 01 '14 13:12

will


Video Answer


2 Answers

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).

like image 170
Duncan Avatar answered Oct 25 '22 06:10

Duncan


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.

like image 36
hpaulj Avatar answered Oct 25 '22 04:10

hpaulj