Idea: Allow assigning a scalar to all elements of a list slice, just as is allowed with a numpy array

It is easy to assign the same scalar value to every element of a slice of a numpy array, but not so easy for a python list: somenparray[start::step]=42
Doing that to a slice of python list requires specifying how many elements are in the slice: somelist[start::step]=lenslice*[42] # You have to figure out lenslice
and create a list of that length.

Further example:

import numpy as np
    
def lenpositiverange(start,limit,step): # how many items in the slice or range for posiive steps
    return (limit - start + step - 1 ) // step 

n=10000    
npbools = np.ones((n,),dtype=bool) 
bools=n*[True] 

npbools[49::7] = False # Valid way to assign scalar to every element of a slice of an numpy array.
# Note that the slice parameters do not need to specify the upper limit.

# bools[49::7] = False  # TypeError: must assign iterable to extended slice
# bools[49::7]= [False] # ValueError: attempt to assign sequence of size 1 to extended slice of size 1422
bools[49:n:7]=lenpositiverange(49,n,7) * [False] # Valid, must specify the upper limit. This is inconvenient.

Request: Allow assigning a scalar to all elements of a list slice, just as is allowed with a numpy array.

This is not compatible with the semantics of the list type.

$ python
Python 3.11.3 (main, Apr  5 2023, 00:00:00) [GCC 13.0.1 20230401 (Red Hat 13.0.1-0)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> lists = [[1, 2], [2, 3], [3, 4]]
>>> lists[::2] = [1, 2] # already valid and does something different than what you're requesting
>>> lists
[1, [2, 3], 2]
1 Like

You do have to work out the length of the slice, but you could just write some code to do that once rather than every time. For example this is what I thought of:

class SetScalar:
    def __init__(self, seq):
        self.seq = seq
        
    def __setitem__(self, index, value):
        if isinstance(index, slice):
            self.seq[index] = [value] * len(range(*index.indices(len(self.seq)))) 
        else:
            self.seq[index] = value

Then to see it in use:

>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> SetScalar(nums)[5:] = 0
>>> nums
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
>>> SetScalar(nums)[-1:0:-2] = 99
>>> nums
[1, 99, 3, 99, 5, 99, 0, 99, 0, 99]
>>> SetScalar(nums)[2] = 42
>>> nums
[1, 99, 42, 99, 5, 99, 0, 99, 0, 99]

Possibly a bit black-magicky, but it makes the intent explicit and you don’t have to worry about any fancy calculations (your lenpositiverange() breaks with negative strides) or repeat yourself.

1 Like

Presumably, broadcasting rules would apply. This isn’t exactly uncharted territory, assuming lists would map roughly to arrays of object type perhaps?

Thank you so much. If there were a place to click “Solution to Problem”, I would.
This solution is dunder-magicky rather than black-magicky. :grinning:

How do you define the broadcasting rules in a backwards-compatible way?

1 Like