Design history of "slicing"

Greetings, I want to know more about the design history of object slicings.

As in Python 3.13.0:

>>> [1:2:3]
  File "<python-input-0>", line 1
    [1:2:3]
      ^
SyntaxError: invalid syntax
>>> [::]
  File "<python-input-2>", line 1
    [::]
     ^
SyntaxError: invalid syntax
>>> [::,]  # Note the comma
  File "<python-input-1>", line 1
    [::,]  # Note the comma
     ^
SyntaxError: invalid syntax

In the meantime:

>>> class T:
...     def __getitem__(self, item):
...         return item
...
>>> s = T()
>>> s[1:2:3]  # Works perfectly fine
slice(1, 2, 3)
>>> s[::]  # Works perfectly fine
slice(None, None, None)
>>> s[::,]
(slice(None, None, None),)  # !?
>>> s[::, ::]  # Why do we even allow this? (Why not SyntaxError?)
(slice(None, None, None), slice(None, None, None))
>>> s[:,:]
(slice(None, None, None), slice(None, None, None))

According to the Python Language Reference:

[…] If the slice list contains at least one comma, the key is a tuple containing the conversion of the slice items; otherwise, the conversion of the lone slice item is the key. The conversion of a slice item that is an expression is that expression. […]

Hence, my question is: Why did we design the object slicing syntax like this?

  • Why did we allow s[::,], s[::, ::] compared to raising a SyntaxError? What are the use cases anyway?

  • Why did we allow s[::,] but not [::,]? (I know object slicing and list expressions serve different purposes with similar syntax, so I understand why do we allow s[::] but not [::]. But if the comma makes it a tuple, why don’t we allow [::,] too? Maybe make :: a single element of the list or something like that?)

(The Disign and History FAQ does not talk about it. I also can’t find the PEP which introduces the slicing syntax. Correct me if I am wrong:) )

  • Why did we allow s[::,], s[::, ::] compared to raising a SyntaxError? What are the use cases anyway?

I don’t know if that was the use case in mind but that’s something used in multidimensional indexing (in libraries like numpy).

In [1]: import numpy as np

In [2]: arr = np.arange(100).reshape(10, 10)

In [3]: arr
Out[3]:
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
       [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])

In [4]: arr[2:8:3]
Out[4]:
array([[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59]])

In [5]: arr[2:8:3, 3:9:2]
Out[5]:
array([[23, 25, 27],
       [53, 55, 57]])
1 Like