slice

Slices, Ranges and __getitem__()

One annoyance when implementing __getitem__() is debugging the slice syntax interactively .. Below you can see an example.

First you create a dummy class with simple method that print its arguments.

Then you can experiment.

class Blah: 
  def __getitem__(self,*args): 
    print(args) 
    return args

b = Blah()
b[1]
b[2:5]
b[::2,2:11:2]

s = b[::3]

print()

#filling the indices
ind = s[0].indices(12)
print(f'indices: {s[0]} => {ind}')

#make it a range
rng = range(*s[0].indices(12))
print(f'range: {rng}')

for i in rng: print(i)
(1,)
(slice(2, 5, None),)
((slice(None, None, 2), slice(2, 11, 2)),)
(slice(None, None, 3),)

indecies: slice(None, None, 3) => (0, 12, 3)
range: range(0, 12, 3)
0
3
6
9

In addition inside your method you can convert a slice to a range by using the .indices() method … and then use them to do processing in a loop.

JS: Copy array vs Reference

Normally when you pass Arrays around in JavaScript and in every other language they are passed by reference. That is a sane default, but sometimes you want to use or make a copy, so that original is not modified when we do changes.

Here is how by-reference work :

a = [1,2,3,4,5]

b = a
b[2] = 55

console.log(a)

------

[ 1, 2, 55, 4, 5 ]

… changing b changes a too.

But if we copy the array a stays the same.

Below I present two ways to copy Arrays in JavaScript via slicing or using the splat/triple-dot-operator.

a = [1,2,3,4,5,[6,[7],8],9]

b = a.slice()
b[2] = 55

console.log('slice-copy : ')
console.log(a)

a = [1,2,3,4,5,[6,[7],8],9]

b = new Array(...a)
b[2] = 55

console.log('... copy : ')
console.log(a)

-----

slice-copy : 
[ 1, 2, 3, 4, 5, [ 6, [ 7 ], 8 ], 9 ]
... copy : 
[ 1, 2, 3, 4, 5, [ 6, [ 7 ], 8 ], 9 ]

This also works for objects/hashes

a = { 'key1' : 'val1', 'key2' : 'val2' }

b = a //reference

b['key1'] = 'val55'

console.log(a)

a = { 'key1' : 'val1', 'key2' : 'val2' }

b = { ... a } //copy

b['key1'] = 'val55'

console.log(a)

-----

{ key1: 'val55', key2: 'val2' }
{ key1: 'val1', key2: 'val2' }

Slice to Range

Sometimes inside __getitem__() method you may need to convert the “slice” coming as argument to a “range” :

#convert slice to range
def slice2range(s):	
	start = 0 if s.start is None else s.start
	step = 1 if s.step is None else s.step
	end = step if s.stop is None else s.stop
	return range(start,end,step)

#inside your code it will be something like this :
def __getitem__(self, item):
        if isinstance(item, slice):
        ....

Example :

: slice2range(slice(2,5))                                                                                                                                            
: range(2, 5)

: slice2range(slice(2,5,1))                                                                                                                                          
: range(2, 5)

: slice2range(slice(5))                                                                                                                                              
: range(0, 5)