
Python slicing looks really weird. How do I explain *s=list(range(100)); s[10:20] *gives you a part of the list. Can we have an extra function for lists and string (and wherever slicing works) to explicitly mention that we're slicing?? Something like - *s=list(range(100)); s.slice(10, 20).* Has this been mentioned before? What would be a drawback of having something more readable like a *.slice* function? Would love to know your thoughts. PS: Explicit is better than Implicit :)

Hi Siddharth and Bruce The original poster asked for something like:
For what it's worth, I think such a thing could be useful. And that experience of it's use would be needed, before it could be added to Python. And here's how to obtain such experience. Bruce has pointed out that we can already get this output:
lst[slice(10, 20)] [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
For what it's worth, here's another way:
lst.__getitem__(slice(10, 20)) [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
We can refactor the original poster's request into two parts. 1. A function, obtained from lst, with a particular property. 2. That this function be obtained in a particular way. We can achieve the first right now:
And we don't need to store the intermediate result:
An alternative approach is to subclass list (which we're allowed to do):
I hope this helps. -- Jonathan

On Thu, Dec 12, 2019 at 04:17:58PM +0530, Siddharth Prajosh wrote:
Python slicing looks really weird.
I disagree that it looks weird. I think it looks beautiful.
How do I explain *s=list(range(100)); s[10:20] *gives you a part of the list.
The same way that you explain that the ^ operator does bitwise-XOR, that the ** operator does exponentiation, that 0x10FF is an integer in hexadecimal and that -23.84e15 is a float: you describe the syntax and describe what it does. Slicing syntax is one of the oldest, most fundamental parts of Python. We're not going to change it. If you need to read Python code, you need to be able to read slicing notation.
Can we have an extra function for lists and string (and wherever slicing works) to explicitly mention that we're slicing??
Slicing syntax already *explicitly* tells us that we're slicing, just as function call syntax explicitly tells us that we're calling a function, and the `+` operator explicitly tells us that we're performing addition (or concatenation). We don't need a .slice() method for the same reason we don't need every class to define a .equals() method. Syntax does the job fine. If you can read function call notation: something(a,b) you should be able to read slicing notation: something[a:b] P.S. "explicit" doesn't necessary mean "spelled out in English words", or "syntax that I like". Symbols can be explicit too. Syntax is explicit. -- Steven

Hi Steve You wrote that we don't need a seq.slice(a, b) method, because the existing syntax does the job fine. To change the subject slightly, it seems to me that we don't need both seq[a:b] and slice(a, b). Certainly, we can get by without either. If we didn't have seq[a:b] we could use slice(a, b) instead:
And if we have seq[a:b] then we can get our hands on slice(a, b):
A purist might say that seq[a:b] is syntactic sugar for seq[slice(a,b)]. And that it is yet another use of the colon. However, I do find it convenient and familiar. So I'd like to keep it, and will defend it against purists who wish to remove it. In that, Steve and I (and I think the original poster) agree. I don't think I'd get the same benefit for seq.slice(a, b) being added to the methods of the built-in sequence types (such as list). But I'd prefer that the decision would be on pragmatic grounds, based on experience. I certainly think the meaning of seq.slice(a, b) is clear, and would more easily discovered than the seq[a:b] notation. Certainly, neither 'slice' nor ':' appear in
help(list)
A personal interest aside: A list is one-dimensional. Numpy deals with multi-dimensional rectangular arrays of numbers. In my mathematics area, I deal with triangles of numbers (think Pascal's Triangle), and also pyramids of numbers. (aside continued). Numpy allows 'slicing' and reshaping of its rectangular arrays. In my mathematics area, I'd like to similarly be able to slice triangles and pyramids of numbers. For example, the central binomial coefficients: http://oeis.org/A000984 Finally, off-topic, I find it a bit odd that in the below we get syntax errors (rather than a run-time error).
-- Jonathan

On Dec 12, 2019, at 09:09, Jonathan Fine <jfine2358@gmail.com> wrote:
What would you expect these to return? For the empty one, you obviously can’t distinguish between no colons separating nothing and any other empty space. (In fact, notice that this is similar to the way tuples of 1 element require the trailing comma and tuples of 0 elements require the parentheses, because otherwise everything and even nothing would be ambiguous as a tuple display, and you couldn’t parse anything until you’d leaned how get no tea and tea in your inventory at the same time.) For the fourth one, slices aren’t just arbitrary sequences of numbers, they’re a start, stop, and step value (and at least the stop has to be specified). It shouldn’t be surprising that the constructor signature looks very similar to the one for range. And the syntax just follows that constructor signature. What would the start, stop, and step be for 1:2:3:4, and where would the extra value be stored? If you do want to index with arbitrary sequences of numbers, just abuse tuples instead of slices: getaxes = getslice getaxes[1, 2, 3, 4] If you already need to use tuples for dimensions and also need something to abuse for some other purpose, then I guess you’re out of luck and will have to write your own thing that takes a few characters to spell instead of one: class T(list): pass getaxes[T(1,2,3,4)] But I think at that point you’re probably better off explicitly using index arrays and bool arrays as your indexes, as numpy does.

On Dec 12, 2019, at 10:19, Ricky Teachey <ricky@teachey.org> wrote:
It already is the same: >>> ((((())))) () So presumably it would still be the same in that universe. :) I don’t see any problem with a[] being the same as a[()]. We already have a[1,] is the same as a[(1,)] rather than a[1], and this case wouldn’t even have that potential for confusion. There are presumably historical reasons why it turned out this way, but if you were designing a new language that had tuple and slice and ellipsis indexing like current Python, would you expect [] to be anything other than [()], or find it confusing?

On Thu, Dec 12, 2019 at 02:14:09PM -0500, Ricky Teachey wrote:
`__new__` is normally written with `cls` as the first argument, since it receives the class as first argument, not self (which doesn't exist yet).
t = (1,2,3) tuple(t) # returns (1,2,3)
tuple(t) isn't the same as tuple(1, 2, 3). Try it and see. tuple(t) passes a single argument to the tuple() constructor, which in the event that the argument is already tuple, returns it unchanged.
MyWeirdTuple(t) # is t the first argument, or is 1 the first argument?
t is unambiguously the first argument. -- Steven

On Dec 12, 2019, at 11:14, Ricky Teachey <ricky@teachey.org> wrote:
That’s not even about subscripting syntax, it’s about calling syntax, and I can’t see why you’d want to change that (or change how type.__call__ delegates to __new__ and __init__, or give tuple a weird metaclass that overrides that, or anything else relevant) just because you changed subscripting. So t would be the first argument, the same as always. If you want 1 to be the first argument, you have to write MyWeirdTuple(*t).

On Thu, Dec 12, 2019 at 11:01:54AM -0800, Andrew Barnert via Python-ideas wrote:
I don’t see any problem with a[] being the same as a[()]. We already have a[1,] is the same as a[(1,)] rather than a[1]
A comma is not an "index argument seperator". a[1,] is not the same as a(1,) where the comma seperates arguments to the function call, and it is permitted to end the call with a seperator. It is equivalent to: temp = 1, # A tuple. a[temp] In the same way, a[2,4,8,16] is the same as temp = 2,4,8,16 # A tuple. a[temp] So a[] would be: temp = # A syntax error. a[temp] not an empty tuple. The status quo is that subscripting syntax `a[ ... ]` requires that the subscript (index, key or slice) inside the square brackets be explicitly given. Even if you give `__getitem__` a default value for the subscript, when using subscript syntax the caller must still provide it explicitly. Your suggestion roughly corresponds to hard-coding into the interpreter the rule that the empty subscript be changed to a default of `()` regardless of what default `__getitem__` is given, or whether it is given a default at all. But why have the empty index default to () rather than None or 0 or -1 or slice(None, None, None) or some other value? That's an arbitrary and not a good one. And why overrule the locally defined default? I think it would be confusing to define: def __getitem__(self, index=0): ... and then have `self[]` pass index=() instead of 0. Even if subscripting honoured the locally defined default, it is still an arbitrary choice with no real justification.
Yes, I would expect it to be a syntax error, because syntactically, a blank token (nothing at all) is not how we create empty tuples. -- Steven

On Thu, Dec 12, 2019 at 4:39 PM Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
nope -- a zero-dimensional array is a scalar -- as distinct from a 1-d (or 2-d, or n-d) array that happens to have only one element. in fact, numpy has a confusing (at least to me) distinction between a 0-D array and a scalar, but that's an implementation detail :-) In [2]: import numpy as np In [3]: # a 3D array In [5]: arr3 = np.ones((2,3,4)) In [6]: arr3.shape Out[6]: (2, 3, 4) In [7]: # index into it yields a 2D array In [8]: arr2 = arr3[1] In [9]: arr2.shape Out[9]: (3, 4) In [10]: # index into it yields a 1D array In [11]: arr1 = arr2[1] In [12]: arr1.shape Out[12]: (4,) In [13]: # index into it yields a 0D array In [14]: arr0 = arr1[0] In [15]: arr0.shape Out[15]: () -CHB - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

We posted at same time. But the reason 0-D array isn't just a Python scalar is because it still has array attributes, most notably dtype. Now maybe 15 years ago NumPy could have created a bunch more custom numeric scalars for the different bit-lengths, but then we still wouldn't have the uniformity of having all the base array methods on those (or if we did, they'd be exactly what we have now, just called "enhanced scalar"). On Thu, Dec 12, 2019 at 8:23 PM Christopher Barker <pythonchb@gmail.com> wrote:
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Thu, Dec 12, 2019 at 7:39 PM Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
If you think of a 1-D array as falling on a line, and a 2-D array as occupying a region of a plane, then the equivalent of a geometric point is a 0-D array. It's not really that useful, but there a a couple minor things you can do:
It's also mutable. Although worrying about a memory allocation on one number is probably silly:
Getting custom dtypes that aren't available in plain Python is probably the best minor feature. But yes, one could easily do all the same things with 1-D arrays of length one. -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Dec 12, 2019, at 16:40, Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
Forgetting about the practical benefits of having a way to have scalars that have a dtype and respond to ndarray methods and so on, here’s how to convince yourself it makes sense mathematically: What do you get when you multiply no numbers together? The multiplicative identity, 1. It’s the same reason that anything to the zeroth power is 1. In fact, it’s directly related. A 3D array where all 3 dimensions are 4 has 4**3 elements. A matrix where both dimensions are 4 has 4**2 elements. A vector where the sole dimension is 4 has 4**1 elements. A scalar where all zero of the dimensions are 4 has 4**0 elements. And if you’re thinking, hey, can’t you also say that all zero of the dimensions are 137? Sure, and the scalar also has 137**0 elements. You can say all zero of the dimensions are any number you like, and you get that number to the zeroth power elements, which is always 1.

On 13/12/19 9:59 am, Steven D'Aprano wrote:
That's the way it is now, but it doesn't have to be that way. There is some logic to the proposal. Currently the argument passed to __getitem__ is what you would get by replacing the square brackets with round ones -- *except* in the case of no arguments. -- Greg

Hi Andrew (We're still a bit off-topic here.) You asked me what I'd expect, if not the present values of:
seq[] SyntaxError: invalid syntax
getslice[1:2:3:4] SyntaxError: invalid syntax
I'd have expected to got respectively the outcome of
seq.__getitem__() seq.__getitem__(slice(1, 2, 3, 4))
In other words, a run-time error rather than a syntax error. And for the slice to make sense, perhaps also to have instead something like:
seq.__getitem__(seq.__slice__(1, 2, 3, 4))
This would allow collections to have a custom slice class. And this would perhaps open the door to
seq[0, 1, key=3]
no longer being a SyntaxError. And now we're well off-topic. (I'll start a new thread for this, if asked off-list to do so.) -- Jonathan

On Dec 12, 2019, at 11:21, Jonathan Fine <jfine2358@gmail.com> wrote:
For the first one, why isn’t it an empty slice, or an empty tuple, instead of no argument? The syntax is ambiguous between all three. And the fact that people on this thread have already suggested they’d like the empty tuple, while you’d like nothing, looks like good evidence that it’s ambiguous to people, not just parsers. For the second one, though, on reflection that would make perfect sense. Making the parser handling one or more colons instead of one or two colons shouldn’t be a problem. And it’s probably only because of historical reasons (Python used to have __getitem__(a) for [a] and __getslice__(a,b) for [a:b], and there were no three-item slices or tuples or ellipsis or anything) that it’s handled the way it is. If we’d always got a “TypeError: slice expected at least 1 argument, got 0” and someone pointed out that we could trivially detect this at compile time and raise a SyntaxError instead, would people go for that, or argue that there’s no need, because catching things at runtime is fine everywhere else in Python? I think the latter. But, that being said, arguing that there’s a need to _undo_ that is just as hard as arguing that there’d be a need to _do_ it would have been. It’s good enough either way, so just keep doing the one we’ve been doing. [snip]
How? There’s no slices there at all, there’s a tuple with non-slice elements, and surely however you want to handle keywords, they’re an extension to tuple syntax, not slice syntax. (Unless you want to allow key:value=1:3 or something?) Anyway, I think if I were designing a language from scratch, I’d get rid of all the sugar on indexing. seq[0, 1, key=3] is just seq.__getitem__(0, 1, key=3), and seq[1:2, ..., 3, 4:-3:-1] is just seq.__getitem__(1:2, ..., 3, 4:-3:-1]. And that means 1:2 has to be a valid expression on its own, but why not? Maybe I’d spell it 1..2 (and then 1...2 means a closed slice rather than a half-open one). Then merge slices with ranges. (What is the range “eggs”:”spam”? It’s a range you can use to slice a sorteddict, but if you try to iterate it or contains it you get a ValueError.) Now we’ve got something as convenient to use as Python, and more convenient for people implementing sequence and mapping types (no more “if it’s a tuple do this, else do that”, it’s just always *args, as it should be), and more flexible.

On Dec 12, 2019, at 05:29, Siddharth Prajosh <sprajosh@gmail.com> wrote:
Python slicing looks really weird. How do I explain s=list(range(100)); s[10:20] gives you a part of the list.
Well, first you have to explain what “slicing” means, and that for lists it returns a new copy of the sub-list, and that it’s a half-open range that includes the 10th element but not the 20th, and that 10th and 20th are counted from 0 rather than from 1, and that indexing (including slicing) is spelled with square brackets rather than subscripts like in math, and so on, because a non-programmer isn’t going to know any of those things. Once you explain all that, I don’t see why explaining that slicing is spelled with a colon is any more difficult.
Can we have an extra function for lists and string (and wherever slicing works)
“Wherever slicing works” includes zillions of third-party types. How are you going to change all of those? And how do you want to spell something like the numpy multidimensional indexing a[10:20, 3, ...,10:20]? Or the dpath (or one of those json-dogging libraries; I always mix them up) search a[“thingies”, 1:3, “name”] (which is roughly equivalent to [thingy[“name”] for thingy in a[“thingies”][1:3]])?
to explicitly mention that we're slicing?? Something like - s=list(range(100)); s.slice(10, 20).
Has this been mentioned before? What would be a drawback of having something more readable like a .slice function?
Since you’re quoting the Zen, TOOWTDI is the obvious answer here. Everyone would still have to learn slicing syntax to read the zillions of existing lines of Python that use it, and all the new code people will continue to write. But now they’d also have to learn to read the slice method. That’s more to learn, not less. Plus, everyone who implements a sequence type would have to implement both ways of doing it. (Since slicing isn’t required to be a sequence, the Sequence ABC probably couldn’t check this for you, and, when used as a mixin, couldn’t implement it for you.)

On Thu, Dec 12, 2019 at 5:29 AM Siddharth Prajosh <sprajosh@gmail.com> wrote:
This exists already as ``itertools.islice`` and, to some extent, ``operator.itemgetter``. https://docs.python.org/3/library/itertools.html#itertools.islice https://docs.python.org/3/library/operator.html#operator.itemgetter As a bonus, ``islice`` is more efficient in many situations.

Hi Siddharth and Bruce The original poster asked for something like:
For what it's worth, I think such a thing could be useful. And that experience of it's use would be needed, before it could be added to Python. And here's how to obtain such experience. Bruce has pointed out that we can already get this output:
lst[slice(10, 20)] [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
For what it's worth, here's another way:
lst.__getitem__(slice(10, 20)) [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
We can refactor the original poster's request into two parts. 1. A function, obtained from lst, with a particular property. 2. That this function be obtained in a particular way. We can achieve the first right now:
And we don't need to store the intermediate result:
An alternative approach is to subclass list (which we're allowed to do):
I hope this helps. -- Jonathan

On Thu, Dec 12, 2019 at 04:17:58PM +0530, Siddharth Prajosh wrote:
Python slicing looks really weird.
I disagree that it looks weird. I think it looks beautiful.
How do I explain *s=list(range(100)); s[10:20] *gives you a part of the list.
The same way that you explain that the ^ operator does bitwise-XOR, that the ** operator does exponentiation, that 0x10FF is an integer in hexadecimal and that -23.84e15 is a float: you describe the syntax and describe what it does. Slicing syntax is one of the oldest, most fundamental parts of Python. We're not going to change it. If you need to read Python code, you need to be able to read slicing notation.
Can we have an extra function for lists and string (and wherever slicing works) to explicitly mention that we're slicing??
Slicing syntax already *explicitly* tells us that we're slicing, just as function call syntax explicitly tells us that we're calling a function, and the `+` operator explicitly tells us that we're performing addition (or concatenation). We don't need a .slice() method for the same reason we don't need every class to define a .equals() method. Syntax does the job fine. If you can read function call notation: something(a,b) you should be able to read slicing notation: something[a:b] P.S. "explicit" doesn't necessary mean "spelled out in English words", or "syntax that I like". Symbols can be explicit too. Syntax is explicit. -- Steven

Hi Steve You wrote that we don't need a seq.slice(a, b) method, because the existing syntax does the job fine. To change the subject slightly, it seems to me that we don't need both seq[a:b] and slice(a, b). Certainly, we can get by without either. If we didn't have seq[a:b] we could use slice(a, b) instead:
And if we have seq[a:b] then we can get our hands on slice(a, b):
A purist might say that seq[a:b] is syntactic sugar for seq[slice(a,b)]. And that it is yet another use of the colon. However, I do find it convenient and familiar. So I'd like to keep it, and will defend it against purists who wish to remove it. In that, Steve and I (and I think the original poster) agree. I don't think I'd get the same benefit for seq.slice(a, b) being added to the methods of the built-in sequence types (such as list). But I'd prefer that the decision would be on pragmatic grounds, based on experience. I certainly think the meaning of seq.slice(a, b) is clear, and would more easily discovered than the seq[a:b] notation. Certainly, neither 'slice' nor ':' appear in
help(list)
A personal interest aside: A list is one-dimensional. Numpy deals with multi-dimensional rectangular arrays of numbers. In my mathematics area, I deal with triangles of numbers (think Pascal's Triangle), and also pyramids of numbers. (aside continued). Numpy allows 'slicing' and reshaping of its rectangular arrays. In my mathematics area, I'd like to similarly be able to slice triangles and pyramids of numbers. For example, the central binomial coefficients: http://oeis.org/A000984 Finally, off-topic, I find it a bit odd that in the below we get syntax errors (rather than a run-time error).
-- Jonathan

On Dec 12, 2019, at 09:09, Jonathan Fine <jfine2358@gmail.com> wrote:
What would you expect these to return? For the empty one, you obviously can’t distinguish between no colons separating nothing and any other empty space. (In fact, notice that this is similar to the way tuples of 1 element require the trailing comma and tuples of 0 elements require the parentheses, because otherwise everything and even nothing would be ambiguous as a tuple display, and you couldn’t parse anything until you’d leaned how get no tea and tea in your inventory at the same time.) For the fourth one, slices aren’t just arbitrary sequences of numbers, they’re a start, stop, and step value (and at least the stop has to be specified). It shouldn’t be surprising that the constructor signature looks very similar to the one for range. And the syntax just follows that constructor signature. What would the start, stop, and step be for 1:2:3:4, and where would the extra value be stored? If you do want to index with arbitrary sequences of numbers, just abuse tuples instead of slices: getaxes = getslice getaxes[1, 2, 3, 4] If you already need to use tuples for dimensions and also need something to abuse for some other purpose, then I guess you’re out of luck and will have to write your own thing that takes a few characters to spell instead of one: class T(list): pass getaxes[T(1,2,3,4)] But I think at that point you’re probably better off explicitly using index arrays and bool arrays as your indexes, as numpy does.

On Dec 12, 2019, at 10:19, Ricky Teachey <ricky@teachey.org> wrote:
It already is the same: >>> ((((())))) () So presumably it would still be the same in that universe. :) I don’t see any problem with a[] being the same as a[()]. We already have a[1,] is the same as a[(1,)] rather than a[1], and this case wouldn’t even have that potential for confusion. There are presumably historical reasons why it turned out this way, but if you were designing a new language that had tuple and slice and ellipsis indexing like current Python, would you expect [] to be anything other than [()], or find it confusing?

On Thu, Dec 12, 2019 at 02:14:09PM -0500, Ricky Teachey wrote:
`__new__` is normally written with `cls` as the first argument, since it receives the class as first argument, not self (which doesn't exist yet).
t = (1,2,3) tuple(t) # returns (1,2,3)
tuple(t) isn't the same as tuple(1, 2, 3). Try it and see. tuple(t) passes a single argument to the tuple() constructor, which in the event that the argument is already tuple, returns it unchanged.
MyWeirdTuple(t) # is t the first argument, or is 1 the first argument?
t is unambiguously the first argument. -- Steven

On Dec 12, 2019, at 11:14, Ricky Teachey <ricky@teachey.org> wrote:
That’s not even about subscripting syntax, it’s about calling syntax, and I can’t see why you’d want to change that (or change how type.__call__ delegates to __new__ and __init__, or give tuple a weird metaclass that overrides that, or anything else relevant) just because you changed subscripting. So t would be the first argument, the same as always. If you want 1 to be the first argument, you have to write MyWeirdTuple(*t).

On Thu, Dec 12, 2019 at 11:01:54AM -0800, Andrew Barnert via Python-ideas wrote:
I don’t see any problem with a[] being the same as a[()]. We already have a[1,] is the same as a[(1,)] rather than a[1]
A comma is not an "index argument seperator". a[1,] is not the same as a(1,) where the comma seperates arguments to the function call, and it is permitted to end the call with a seperator. It is equivalent to: temp = 1, # A tuple. a[temp] In the same way, a[2,4,8,16] is the same as temp = 2,4,8,16 # A tuple. a[temp] So a[] would be: temp = # A syntax error. a[temp] not an empty tuple. The status quo is that subscripting syntax `a[ ... ]` requires that the subscript (index, key or slice) inside the square brackets be explicitly given. Even if you give `__getitem__` a default value for the subscript, when using subscript syntax the caller must still provide it explicitly. Your suggestion roughly corresponds to hard-coding into the interpreter the rule that the empty subscript be changed to a default of `()` regardless of what default `__getitem__` is given, or whether it is given a default at all. But why have the empty index default to () rather than None or 0 or -1 or slice(None, None, None) or some other value? That's an arbitrary and not a good one. And why overrule the locally defined default? I think it would be confusing to define: def __getitem__(self, index=0): ... and then have `self[]` pass index=() instead of 0. Even if subscripting honoured the locally defined default, it is still an arbitrary choice with no real justification.
Yes, I would expect it to be a syntax error, because syntactically, a blank token (nothing at all) is not how we create empty tuples. -- Steven

On Thu, Dec 12, 2019 at 4:39 PM Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
nope -- a zero-dimensional array is a scalar -- as distinct from a 1-d (or 2-d, or n-d) array that happens to have only one element. in fact, numpy has a confusing (at least to me) distinction between a 0-D array and a scalar, but that's an implementation detail :-) In [2]: import numpy as np In [3]: # a 3D array In [5]: arr3 = np.ones((2,3,4)) In [6]: arr3.shape Out[6]: (2, 3, 4) In [7]: # index into it yields a 2D array In [8]: arr2 = arr3[1] In [9]: arr2.shape Out[9]: (3, 4) In [10]: # index into it yields a 1D array In [11]: arr1 = arr2[1] In [12]: arr1.shape Out[12]: (4,) In [13]: # index into it yields a 0D array In [14]: arr0 = arr1[0] In [15]: arr0.shape Out[15]: () -CHB - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

We posted at same time. But the reason 0-D array isn't just a Python scalar is because it still has array attributes, most notably dtype. Now maybe 15 years ago NumPy could have created a bunch more custom numeric scalars for the different bit-lengths, but then we still wouldn't have the uniformity of having all the base array methods on those (or if we did, they'd be exactly what we have now, just called "enhanced scalar"). On Thu, Dec 12, 2019 at 8:23 PM Christopher Barker <pythonchb@gmail.com> wrote:
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Thu, Dec 12, 2019 at 7:39 PM Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
If you think of a 1-D array as falling on a line, and a 2-D array as occupying a region of a plane, then the equivalent of a geometric point is a 0-D array. It's not really that useful, but there a a couple minor things you can do:
It's also mutable. Although worrying about a memory allocation on one number is probably silly:
Getting custom dtypes that aren't available in plain Python is probably the best minor feature. But yes, one could easily do all the same things with 1-D arrays of length one. -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Dec 12, 2019, at 16:40, Steven D'Aprano <steve@pearwood.info> wrote:
Surely a zero-dimensional array ought to have no elements at all?
Forgetting about the practical benefits of having a way to have scalars that have a dtype and respond to ndarray methods and so on, here’s how to convince yourself it makes sense mathematically: What do you get when you multiply no numbers together? The multiplicative identity, 1. It’s the same reason that anything to the zeroth power is 1. In fact, it’s directly related. A 3D array where all 3 dimensions are 4 has 4**3 elements. A matrix where both dimensions are 4 has 4**2 elements. A vector where the sole dimension is 4 has 4**1 elements. A scalar where all zero of the dimensions are 4 has 4**0 elements. And if you’re thinking, hey, can’t you also say that all zero of the dimensions are 137? Sure, and the scalar also has 137**0 elements. You can say all zero of the dimensions are any number you like, and you get that number to the zeroth power elements, which is always 1.

On 13/12/19 9:59 am, Steven D'Aprano wrote:
That's the way it is now, but it doesn't have to be that way. There is some logic to the proposal. Currently the argument passed to __getitem__ is what you would get by replacing the square brackets with round ones -- *except* in the case of no arguments. -- Greg

Hi Andrew (We're still a bit off-topic here.) You asked me what I'd expect, if not the present values of:
seq[] SyntaxError: invalid syntax
getslice[1:2:3:4] SyntaxError: invalid syntax
I'd have expected to got respectively the outcome of
seq.__getitem__() seq.__getitem__(slice(1, 2, 3, 4))
In other words, a run-time error rather than a syntax error. And for the slice to make sense, perhaps also to have instead something like:
seq.__getitem__(seq.__slice__(1, 2, 3, 4))
This would allow collections to have a custom slice class. And this would perhaps open the door to
seq[0, 1, key=3]
no longer being a SyntaxError. And now we're well off-topic. (I'll start a new thread for this, if asked off-list to do so.) -- Jonathan

On Dec 12, 2019, at 11:21, Jonathan Fine <jfine2358@gmail.com> wrote:
For the first one, why isn’t it an empty slice, or an empty tuple, instead of no argument? The syntax is ambiguous between all three. And the fact that people on this thread have already suggested they’d like the empty tuple, while you’d like nothing, looks like good evidence that it’s ambiguous to people, not just parsers. For the second one, though, on reflection that would make perfect sense. Making the parser handling one or more colons instead of one or two colons shouldn’t be a problem. And it’s probably only because of historical reasons (Python used to have __getitem__(a) for [a] and __getslice__(a,b) for [a:b], and there were no three-item slices or tuples or ellipsis or anything) that it’s handled the way it is. If we’d always got a “TypeError: slice expected at least 1 argument, got 0” and someone pointed out that we could trivially detect this at compile time and raise a SyntaxError instead, would people go for that, or argue that there’s no need, because catching things at runtime is fine everywhere else in Python? I think the latter. But, that being said, arguing that there’s a need to _undo_ that is just as hard as arguing that there’d be a need to _do_ it would have been. It’s good enough either way, so just keep doing the one we’ve been doing. [snip]
How? There’s no slices there at all, there’s a tuple with non-slice elements, and surely however you want to handle keywords, they’re an extension to tuple syntax, not slice syntax. (Unless you want to allow key:value=1:3 or something?) Anyway, I think if I were designing a language from scratch, I’d get rid of all the sugar on indexing. seq[0, 1, key=3] is just seq.__getitem__(0, 1, key=3), and seq[1:2, ..., 3, 4:-3:-1] is just seq.__getitem__(1:2, ..., 3, 4:-3:-1]. And that means 1:2 has to be a valid expression on its own, but why not? Maybe I’d spell it 1..2 (and then 1...2 means a closed slice rather than a half-open one). Then merge slices with ranges. (What is the range “eggs”:”spam”? It’s a range you can use to slice a sorteddict, but if you try to iterate it or contains it you get a ValueError.) Now we’ve got something as convenient to use as Python, and more convenient for people implementing sequence and mapping types (no more “if it’s a tuple do this, else do that”, it’s just always *args, as it should be), and more flexible.

On Dec 12, 2019, at 05:29, Siddharth Prajosh <sprajosh@gmail.com> wrote:
Python slicing looks really weird. How do I explain s=list(range(100)); s[10:20] gives you a part of the list.
Well, first you have to explain what “slicing” means, and that for lists it returns a new copy of the sub-list, and that it’s a half-open range that includes the 10th element but not the 20th, and that 10th and 20th are counted from 0 rather than from 1, and that indexing (including slicing) is spelled with square brackets rather than subscripts like in math, and so on, because a non-programmer isn’t going to know any of those things. Once you explain all that, I don’t see why explaining that slicing is spelled with a colon is any more difficult.
Can we have an extra function for lists and string (and wherever slicing works)
“Wherever slicing works” includes zillions of third-party types. How are you going to change all of those? And how do you want to spell something like the numpy multidimensional indexing a[10:20, 3, ...,10:20]? Or the dpath (or one of those json-dogging libraries; I always mix them up) search a[“thingies”, 1:3, “name”] (which is roughly equivalent to [thingy[“name”] for thingy in a[“thingies”][1:3]])?
to explicitly mention that we're slicing?? Something like - s=list(range(100)); s.slice(10, 20).
Has this been mentioned before? What would be a drawback of having something more readable like a .slice function?
Since you’re quoting the Zen, TOOWTDI is the obvious answer here. Everyone would still have to learn slicing syntax to read the zillions of existing lines of Python that use it, and all the new code people will continue to write. But now they’d also have to learn to read the slice method. That’s more to learn, not less. Plus, everyone who implements a sequence type would have to implement both ways of doing it. (Since slicing isn’t required to be a sequence, the Sequence ABC probably couldn’t check this for you, and, when used as a mixin, couldn’t implement it for you.)

On Thu, Dec 12, 2019 at 5:29 AM Siddharth Prajosh <sprajosh@gmail.com> wrote:
This exists already as ``itertools.islice`` and, to some extent, ``operator.itemgetter``. https://docs.python.org/3/library/itertools.html#itertools.islice https://docs.python.org/3/library/operator.html#operator.itemgetter As a bonus, ``islice`` is more efficient in many situations.
participants (12)
-
Andrew Barnert
-
Bruce Leban
-
Christopher Barker
-
David Mertz
-
Greg Ewing
-
Jonathan Fine
-
Michael Selik
-
MRAB
-
Random832
-
Ricky Teachey
-
Siddharth Prajosh
-
Steven D'Aprano