str(slice(10)) should return "slice(10)"

Currently str(slice(10)) returns "slice(None, 10, None)" If the start and step are None, consider not emitting them. Similarly slice(None) is rendered slice(None, None, None). When you're printing a lot of slices, it's a lot of extra noise.

On Thu, Oct 06, 2016 at 04:19:17PM -0700, Neil Girdhar wrote:
Currently str(slice(10)) returns "slice(None, 10, None)"
If the start and step are None, consider not emitting them. Similarly slice(None) is rendered slice(None, None, None).
When you're printing a lot of slices, it's a lot of extra noise.
I have an alternative suggestion. Wouldn't it be nice if slice objects looked something like the usual slice syntax? If you think the answer is No, then you'll hate my suggestion :-) Let's keep the current repr() of slice objects as they are, using the full function-call syntax complete with all three arguments show explicitly: repr(slice(None, None, None)) => "slice(None, None, None)" But let's make str() of a slice more suggestive of actual slicing, and as a bonus, make slices easier to create too. str(slice(None, None, None)) => "slice[:]" Let the slice type itself be sliceable, as an alternate constuctor: slice[:] => returns slice(None) slice[start:] => returns slice(start, None) slice[:end] => returns slice(None, end) slice[start::step] => returns slice(start, None, step) and so forth. (This probably would require changing the type of slice to a new metaclass.) And then have str() return the compact slice syntax. At worst, the compact slice syntax is one character longer than the optimal function syntax: # proposed slice str() slice[:7] # 9 characters # proposed compact str() slice(7) # 8 characters # current str() slice(None, 7, None) # 20 characters but it will be more compact more often: slice[1:] # 9 characters versus: slice(1, None) # 14 characters slice(None, 1, None) # 20 characters -- Steve

On 12 November 2016 at 10:26, Steven D'Aprano <steve@pearwood.info> wrote:
But let's make str() of a slice more suggestive of actual slicing, and as a bonus, make slices easier to create too.
str(slice(None, None, None)) => "slice[:]"
Let the slice type itself be sliceable, as an alternate constuctor:
slice[:] => returns slice(None) slice[start:] => returns slice(start, None) slice[:end] => returns slice(None, end) slice[start::step] => returns slice(start, None, step)
and so forth. (This probably would require changing the type of slice to a new metaclass.)
And then have str() return the compact slice syntax.
+1, I like this idea, this is very close to what NumPy does. I would also mention http://bugs.python.org/issue24379 -- Ivan

+100 I like this idea of giving `slice` a metaclass that defines a `.__getitem__()` allowing us to construct slices on the slice type itself. FWIW, this is exactly what pandas.IndexSlice does. E.g., from http://pandas.pydata.org/pandas-docs/stable/advanced.html: In [51]: dfmi.loc[(slice('A1','A3'),slice(None), ['C1','C3']),:] In [52]: idx = pd.IndexSlice In [53]: dfmi.loc[idx[:,:,['C1','C3']],idx[:,'foo']] This is one of those nifty things that's buried in Pandas but not well documented. I'd rather spell the above simply as: dfmi.loc[slice[:,:,['C1','C3']], slice[:,'foo']] I like the change proposed to `str(slice(10))` also... and it would be way better if `slice[:10]` were actual "syntax." In fact, in that case it could even be the repr(). Note: Notwithstanding my scare quotes, Steven isn't actually asking for new syntax. "slice" is already a name, and names can already be followed by square brackets. He's just asking for a new method on a metaclass. On Sat, Nov 12, 2016 at 1:26 AM, Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Oct 06, 2016 at 04:19:17PM -0700, Neil Girdhar wrote:
Currently str(slice(10)) returns "slice(None, 10, None)"
If the start and step are None, consider not emitting them. Similarly slice(None) is rendered slice(None, None, None).
When you're printing a lot of slices, it's a lot of extra noise.
I have an alternative suggestion. Wouldn't it be nice if slice objects looked something like the usual slice syntax?
If you think the answer is No, then you'll hate my suggestion :-)
Let's keep the current repr() of slice objects as they are, using the full function-call syntax complete with all three arguments show explicitly:
repr(slice(None, None, None)) => "slice(None, None, None)"
But let's make str() of a slice more suggestive of actual slicing, and as a bonus, make slices easier to create too.
str(slice(None, None, None)) => "slice[:]"
Let the slice type itself be sliceable, as an alternate constuctor:
slice[:] => returns slice(None) slice[start:] => returns slice(start, None) slice[:end] => returns slice(None, end) slice[start::step] => returns slice(start, None, step)
and so forth. (This probably would require changing the type of slice to a new metaclass.)
And then have str() return the compact slice syntax.
At worst, the compact slice syntax is one character longer than the optimal function syntax:
# proposed slice str() slice[:7] # 9 characters
# proposed compact str() slice(7) # 8 characters
# current str() slice(None, 7, None) # 20 characters
but it will be more compact more often:
slice[1:] # 9 characters
versus:
slice(1, None) # 14 characters slice(None, 1, None) # 20 characters
-- Steve _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- 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 Sat, Nov 12, 2016 at 9:10 PM David Mertz <mertz@gnosis.cx> wrote:
dfmi.loc[slice[:,:,['C1','C3']], slice[:,'foo']]
I like the change proposed to `str(slice(10))` also... and it would be way better if `slice[:10]` were actual "syntax." In fact, in that case it could even be the repr().
Indexing operator for classes already has a meaning, for generic types. It is a possibility that slice will become a generic type (see here: https://github.com/python/mypy/issues/2410#issuecomment-258898836) and this syntax will make it either impossible or require Slice[] to be different from slice[] in a potentially confusing way. Elazar

I thought of the use of `.__getitem__()` in metaclasses in the typing module. I feel like this use is more natural and more useful than that. Should we someday need a slice generic type for PEP 484, the spelling would naturally be `Slice[T]` instead, in my mind. But `slice[1:10,2]` should be a constructor for a concrete slice object. On Sat, Nov 12, 2016 at 11:20 AM, אלעזר <elazarg@gmail.com> wrote:
On Sat, Nov 12, 2016 at 9:10 PM David Mertz <mertz@gnosis.cx> wrote:
dfmi.loc[slice[:,:,['C1','C3']], slice[:,'foo']]
I like the change proposed to `str(slice(10))` also... and it would be way better if `slice[:10]` were actual "syntax." In fact, in that case it could even be the repr().
Indexing operator for classes already has a meaning, for generic types. It is a possibility that slice will become a generic type (see here: https://github.com/python/mypy/issues/2410#issuecomment-258898836) and this syntax will make it either impossible or require Slice[] to be different from slice[] in a potentially confusing way.
Elazar
-- 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 12 November 2016 at 20:27, David Mertz <mertz@gnosis.cx> wrote:
I thought of the use of `.__getitem__()` in metaclasses in the typing module. I feel like this use is more natural and more useful than that. Should we someday need a slice generic type for PEP 484, the spelling would naturally be `Slice[T]` instead, in my mind. But `slice[1:10,2]` should be a constructor for a concrete slice object.
Slice[T] vs slice[::-1] is coherent with what we have now for List[T] vs list, etc. -- Ivan

On Sat, Nov 12, 2016 at 11:41 AM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
On 12 November 2016 at 20:27, David Mertz <mertz@gnosis.cx> wrote:
I thought of the use of `.__getitem__()` in metaclasses in the typing module. I feel like this use is more natural and more useful than that. Should we someday need a slice generic type for PEP 484, the spelling would naturally be `Slice[T]` instead, in my mind. But `slice[1:10,2]` should be a constructor for a concrete slice object.
Slice[T] vs slice[::-1] is coherent with what we have now for List[T] vs list, etc.
Not really. We have List[T] but list[x] is invalid -- it doesn't have a different meaning (it's list instances that support indexing). And in fact the distinction between List and list is intentionally minimal -- List is simply what list wants to become when it grows up. :-) Honestly I think the use case of wanting to create a slice object is rare enough that we can continue to write slice(x, y, z). If you really find yourself wanting something shorter, I believe in the past it's been pointed out that you could create a helper, e.g. like this: class S: def __getitem__(self, x): return x s = S() a = s[:():] -- --Guido van Rossum (python.org/~guido)

The very common use case for creating slice objects is in Pandas and similar libraries. Xarray certainly, or Blaze, to a lesser extent NumPy. That said, it's very easy to define a suitable __getitem__, as Guido shows. It's really a question simply of whether that object should be named 'slice' or something else. On Nov 12, 2016 5:08 PM, "Guido van Rossum" <guido@python.org> wrote:
On Sat, Nov 12, 2016 at 11:41 AM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
On 12 November 2016 at 20:27, David Mertz <mertz@gnosis.cx> wrote:
I thought of the use of `.__getitem__()` in metaclasses in the typing module. I feel like this use is more natural and more useful than that. Should we someday need a slice generic type for PEP 484, the spelling would naturally be `Slice[T]` instead, in my mind. But `slice[1:10,2]` should be a constructor for a concrete slice object.
Slice[T] vs slice[::-1] is coherent with what we have now for List[T] vs list, etc.
Not really. We have List[T] but list[x] is invalid -- it doesn't have a different meaning (it's list instances that support indexing). And in fact the distinction between List and list is intentionally minimal -- List is simply what list wants to become when it grows up. :-)
Honestly I think the use case of wanting to create a slice object is rare enough that we can continue to write slice(x, y, z). If you really find yourself wanting something shorter, I believe in the past it's been pointed out that you could create a helper, e.g. like this:
class S: def __getitem__(self, x): return x s = S()
a = s[:():]
-- --Guido van Rossum (python.org/~guido)

If we *do* want the name 'slice' as the spelling for the thing that can either be called or indexed to create a slice object, we could probably use some boilerplate like this: In [1]: class Slice: ...: def __init__(self): ...: self.slice = slice ...: def __getitem__(self, x): ...: return x ...: def __call__(self, *args, **kws): ...: return self.slice(*args, **kws) ...: In [2]: slice = Slice() In [3]: slice(1,10,2) Out[3]: slice(1, 10, 2) In [4]: slice[1:10:2] Out[4]: slice(1, 10, 2) I'm sure there are some less common uses of the name 'slice' that would break here. That's why I'd want an official standard behavior.
The very common use case for creating slice objects is in Pandas and similar libraries. Xarray certainly, or Blaze, to a lesser extent NumPy.
That said, it's very easy to define a suitable __getitem__, as Guido shows. It's really a question simply of whether that object should be named 'slice' or something else. On Nov 12, 2016 5:08 PM, "Guido van Rossum" <guido@python.org> wrote:
Honestly I think the use case of wanting to create a slice object is rare enough that we can continue to write slice(x, y, z). If you really find yourself wanting something shorter, I believe in the past it's been pointed out that you could create a helper, e.g. like this:
-- 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 Nov 12, 2016 5:46 PM, "David Mertz" <mertz@gnosis.cx> wrote:
If we *do* want the name 'slice' as the spelling for the thing that can
either be called or indexed to create a slice object, we could probably use some boilerplate like this:
In [1]: class Slice: ...: def __init__(self): ...: self.slice = slice ...: def __getitem__(self, x): ...: return x ...: def __call__(self, *args, **kws): ...: return self.slice(*args, **kws) ...:
In [2]: slice = Slice()
In [3]: slice(1,10,2) Out[3]: slice(1, 10, 2)
In [4]: slice[1:10:2] Out[4]: slice(1, 10, 2)
I'm sure there are some less common uses of the name 'slice' that would
break here. That's why I'd want an official standard behavior. isinstance(obj, slice) would be a notable one. -n

On Sat, Nov 12, 2016 at 5:46 PM, David Mertz <mertz@gnosis.cx> wrote:
If we *do* want the name 'slice' as the spelling for the thing that can either be called or indexed to create a slice object, we could probably use some boilerplate like this:
I can't stop you from doing that in your own session, but I don't want to reuse the builtin slice object for that. If this is so useful with Pandas maybe the Pandas library can define its own helper for this purpose. -- --Guido van Rossum (python.org/~guido)

Indeed. I mentioned up-thread that pandas names this IndexSlicer, so it does exist. And maybe that's a perfectly fine spelling (users can always give it a shorter name, like ndx) On Nov 12, 2016 7:07 PM, "Guido van Rossum" <guido@python.org> wrote:
On Sat, Nov 12, 2016 at 5:46 PM, David Mertz <mertz@gnosis.cx> wrote:
If we *do* want the name 'slice' as the spelling for the thing that can either be called or indexed to create a slice object, we could probably use some boilerplate like this:
I can't stop you from doing that in your own session, but I don't want to reuse the builtin slice object for that. If this is so useful with Pandas maybe the Pandas library can define its own helper for this purpose.
-- --Guido van Rossum (python.org/~guido)

On 13 November 2016 at 04:07, Guido van Rossum <guido@python.org> wrote:
On Sat, Nov 12, 2016 at 5:46 PM, David Mertz <mertz@gnosis.cx> wrote:
If we *do* want the name 'slice' as the spelling for the thing that can either be called or indexed to create a slice object, we could probably use some boilerplate like this:
I can't stop you from doing that in your own session, but I don't want to reuse the builtin slice object for that. If this is so useful with Pandas maybe the Pandas library can define its own helper for this purpose.
This reminds me @ vs .dot() for matrix multiplication a bit. Pandas has IndexSlicer, NumPy has index_exp, etc. I think it would be nice to have a simple common way to express this. But here we have an additional ingredient -- generic types. I think that a reasonable compromise would be to simply continue the way proposed in http://bugs.python.org/issue24379 -- just add operator.subscript for this purpose. Pros: * subscript is not a class, so that subscript[...] will be not confused with generics; * this does not require patching built-ins; * all libraries that need this will get a "common interface" in stdlib, operator module seems to be good place for this. The patch for operator.subscript was already applied, but it is now reverted because it caused a refleak. I have submitted a new patch that does not cause refleaks, I just replaced empty __slots__ with a __setattr__: it looks like __slots__ are not needed here to save memory (there is only a singleton instance), they were used just to create an immutable object. -- Ivan

Sounds good. On Sun, Nov 13, 2016 at 3:25 AM, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
On 13 November 2016 at 04:07, Guido van Rossum <guido@python.org> wrote:
On Sat, Nov 12, 2016 at 5:46 PM, David Mertz <mertz@gnosis.cx> wrote:
If we *do* want the name 'slice' as the spelling for the thing that can either be called or indexed to create a slice object, we could probably use some boilerplate like this:
I can't stop you from doing that in your own session, but I don't want to reuse the builtin slice object for that. If this is so useful with Pandas maybe the Pandas library can define its own helper for this purpose.
This reminds me @ vs .dot() for matrix multiplication a bit. Pandas has IndexSlicer, NumPy has index_exp, etc. I think it would be nice to have a simple common way to express this. But here we have an additional ingredient -- generic types. I think that a reasonable compromise would be to simply continue the way proposed in http://bugs.python.org/issue24379 -- just add operator.subscript for this purpose. Pros: * subscript is not a class, so that subscript[...] will be not confused with generics; * this does not require patching built-ins; * all libraries that need this will get a "common interface" in stdlib, operator module seems to be good place for this.
The patch for operator.subscript was already applied, but it is now reverted because it caused a refleak. I have submitted a new patch that does not cause refleaks, I just replaced empty __slots__ with a __setattr__: it looks like __slots__ are not needed here to save memory (there is only a singleton instance), they were used just to create an immutable object.
-- Ivan
-- --Guido van Rossum (python.org/~guido)

On 13 November 2016 at 21:25, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
This reminds me @ vs .dot() for matrix multiplication a bit. Pandas has IndexSlicer, NumPy has index_exp, etc. I think it would be nice to have a simple common way to express this. But here we have an additional ingredient -- generic types. I think that a reasonable compromise would be to simply continue the way proposed in http://bugs.python.org/issue24379 -- just add operator.subscript for this purpose. Pros: * subscript is not a class, so that subscript[...] will be not confused with generics; * this does not require patching built-ins; * all libraries that need this will get a "common interface" in stdlib, operator module seems to be good place for this.
From an educational point of view, it also makes it a bit easier to give interactive examples of how slicing syntax maps to the subscript parameter on __getitem__, __setitem__ and __delitem__: you can just do "print(operator.subscript[EXPR])" rather than having to build a dummy __getitem__ implementation of your own.
If an actual use case is found for it, that approach would also leave "operator.subscript('EXPR')" available for a micro-eval implementation that evaluated a given string as a subscript rather than as a normal top level expression. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sat, Nov 12, 2016 at 11:10 AM, David Mertz <mertz@gnosis.cx> wrote:
+100
I like this idea of giving `slice` a metaclass that defines a `.__getitem__()` allowing us to construct slices on the slice type itself.
FWIW, this is exactly what pandas.IndexSlice does. E.g., from http://pandas.pydata.org/pandas-docs/stable/advanced.html:
Indeed, this would be really nice! The requirement to use a special object to construct slices outside of indexing is a repeated pain-point for me and users of pandas/xarray. To non-experts, it's not at all obvious what slice(None, 3, None) means, but slice[:3] has the familiar syntax. In xarray, we encourage passing around slice objects to do indexing with keyword arguments [1], e.g., data.sel(time=slice(100)) to pull out the first 100 values along the time axis. data.sel(time=slice[:100]) would be a significant improvement. Even if we ever get indexing with keyword arguments in Python, I still like the readability of data[time=slice[:100]] better than data[time=:100], where the colon gets lost. [1] http://xarray.pydata.org/en/stable/indexing.html#indexing-with-labeled-dimen...

But let's make str() of a slice more suggestive of actual slicing, and as a bonus, make slices easier to create too.
str(slice(None, None, None)) => "slice[:]"
following all the later discussion, I'd like to come back to this initial part of this thread: An even briefer for for just "str" would be to omit the 'slice' part str(slice(None, None, None)) => "[:]" etc. Since it has colons in there, it's clear it is a slice. And it is very brief notation. -Alexander

On Sat, Nov 12, 2016 at 7:35 PM, Alexander Heger <python@2sn.net> wrote:
But let's make str() of a slice more suggestive of actual slicing, and
as a bonus, make slices easier to create too.
str(slice(None, None, None)) => "slice[:]"
following all the later discussion, I'd like to come back to this initial part of this thread: An even briefer for for just "str" would be to omit the 'slice' part
str(slice(None, None, None)) => "[:]"
etc. Since it has colons in there, it's clear it is a slice. And it is very brief notation.
It is also confusing -- it is surrounded by [] but it is not a list (not even close). -- --Guido van Rossum (python.org/~guido)
participants (10)
-
Alexander Heger
-
David Mertz
-
Guido van Rossum
-
Ivan Levkivskyi
-
Nathaniel Smith
-
Neil Girdhar
-
Nick Coghlan
-
Stephan Hoyer
-
Steven D'Aprano
-
אלעזר