Re: [Python-ideas] Implement comparison operators for range objects

On Oct 12, 2011 9:37 PM, "Sven Marnach" <sven@marnach.net> wrote:
Steven D'Aprano schrieb am Do, 13. Okt 2011, um 04:33:49 +1100:
When implementing '==' and '!=' for range objects, it would be natural to implement the other comparison operators, too (lexicographically, as for all other sequence types).
I don't agree. Equality makes sense for ranges: two ranges are equal if they have the same start, stop and step values.
No, two ranges should be equal if they represent the same sequence, i.e. if they compare equal when converted to a list:
range(0) == range(4, 4, 4) range(5, 10, 3) == range(5, 11, 3) range(3, 6, 3) == range(3, 4)
But order comparisons don't have any sensible meaning: range objects are numeric ranges, integer-valued intervals, not generic lists, and it is meaningless to say that one range is less than or greater than another.
Well, it's meaningless unless you define what it means. Range objects are equal if they compare equal after converting to a list. You could define '<' or '>' the same way. All built-in sequence types support lexicographical comparison, so I thought it would be natural to bring the only one that behaves differently in line. (Special cases aren't special enough...)
This is just to explain my thoughts, I don't have a strong opinion on this one.
I'll try and prepare a patch for '==' and '!=' and add it to the issue tracker.
Cheers, Sven _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
If you consider a range to represent a special type of set, which it is since it always contains unique values, then comparison operators do make sense. E.g. range(4,8) < range(2,9) is a subset comparison. +1 for equality checks David

On Wed, Oct 12, 2011 at 5:22 PM, David Townshend <aquavitae69@gmail.com> wrote:
On Oct 12, 2011 9:37 PM, "Sven Marnach" <sven@marnach.net> wrote:
Steven D'Aprano schrieb am Do, 13. Okt 2011, um 04:33:49 +1100:
When implementing '==' and '!=' for range objects, it would be natural to implement the other comparison operators, too (lexicographically, as for all other sequence types).
I don't agree. Equality makes sense for ranges: two ranges are equal if they have the same start, stop and step values.
No, two ranges should be equal if they represent the same sequence, i.e. if they compare equal when converted to a list:
range(0) == range(4, 4, 4) range(5, 10, 3) == range(5, 11, 3) range(3, 6, 3) == range(3, 4)
But order comparisons don't have any sensible meaning: range objects are numeric ranges, integer-valued intervals, not generic lists, and it is meaningless to say that one range is less than or greater than another.
Well, it's meaningless unless you define what it means. Range objects are equal if they compare equal after converting to a list. You could define '<' or '>' the same way. All built-in sequence types support lexicographical comparison, so I thought it would be natural to bring the only one that behaves differently in line. (Special cases aren't special enough...)
This is just to explain my thoughts, I don't have a strong opinion on this one.
I'll try and prepare a patch for '==' and '!=' and add it to the issue tracker.
Cheers, Sven _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
If you consider a range to represent a special type of set, which it is since it always contains unique values, then comparison operators do make sense. E.g. range(4,8) < range(2,9) is a subset comparison.
But it's *not* a set. It's got a definite order. range(10) != range(9, -1, -1) even though they contain the same values.
+1 for equality checks
Yeah, we're down to bikeshedding about whether range(0, 10, 2) == range(0, 11, 2). -- --Guido van Rossum (python.org/~guido)

On Thu, Oct 13, 2011 at 11:06 AM, Guido van Rossum <guido@python.org> wrote:
Yeah, we're down to bikeshedding about whether range(0, 10, 2) == range(0, 11, 2).
I'll weigh in on the "compare like a sequence" side, even if the specific range definitions are different. It's the way range comparisons work in Python 2 and I'd like range() objects to be as close to a computationally defined immutable list as we can get them. It may even make sense to make them hashable in those terms. I see it as similar to the fact that "Decimal('1') == Decimal('1.0')" even though those two objects carry additional state regarding significant digits that the definition of equivalence ignores. But not exposing start/stop/step is a definite oversight - I actually thought we *did* expose them, but I was thinking of slice objects. With those attributes exposed, anyone that wants a more restrictive form of equality can easily implement it for themselves. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 13/10/2011 02:43, Nick Coghlan wrote:
On Thu, Oct 13, 2011 at 11:06 AM, Guido van Rossum<guido@python.org> wrote:
Yeah, we're down to bikeshedding about whether range(0, 10, 2) == range(0, 11, 2).
I'll weigh in on the "compare like a sequence" side, even if the specific range definitions are different. It's the way range comparisons work in Python 2 and I'd like range() objects to be as close to a computationally defined immutable list as we can get them. It may even make sense to make them hashable in those terms.
+1

On Thu, Oct 13, 2011 at 03:43, Nick Coghlan <ncoghlan@gmail.com> wrote:
But not exposing start/stop/step is a definite oversight - I actually thought we *did* expose them, but I was thinking of slice objects.
There is already a tracker issue with a patch adding the start/stop/step attributes: http://bugs.python.org/issue9896 Daniel

Nick Coghlan schrieb am Do, 13. Okt 2011, um 11:43:53 +1000:
I'll weigh in on the "compare like a sequence" side, even if the specific range definitions are different. It's the way range comparisons work in Python 2 and I'd like range() objects to be as close to a computationally defined immutable list as we can get them. It may even make sense to make them hashable in those terms.
The current interface of ranges is that of a sequence and nothing more. The only place where the original parameters that were used to create the sequence pop up is in its representation. Giving those parameters more weight seems a bit like remembering the list coomprehension that was used to create a list and take this into account when comparing lists (of course I'm exaggerating here to make a point).
But not exposing start/stop/step is a definite oversight - I actually thought we *did* expose them, but I was thinking of slice objects. With those attributes exposed, anyone that wants a more restrictive form of equality can easily implement it for themselves.
The current implementation of range objects could be both made considerably shorter and sped up a bit by normalising the parameters right from the start. I'd argue that doing so would be a good idea even when start, stop and step are exposed. It would make clear what is happening and remove any ambiguity as to the semantics of range objects in general and comparison of range objects in particular. (A simple implementation is more often than not a good indication for having arrived at the right notions.) Note that using '[:]' does not completely normalise a range object. It only normalises the stop parameter. Cheers, Sven

On Thu, Oct 13, 2011 at 5:27 AM, Sven Marnach <sven@marnach.net> wrote:
The *current interface* of ranges is that of a sequence and nothing more. The only place where the original parameters that were used to create the sequence pop up is in its representation. Giving those parameters more weight seems a bit like remembering the list coomprehension that was used to create a list and take this into account when comparing lists (of course I'm exaggerating here to make a point). *[emphasis added]*
The current interface doesn't include the changes you want to make either. Limiting the consideration of extending the interface of ranges to exactly your proposal is unnecessarily limiting. Suppose we modified ranges to allow changing the step value.
x = range(0,10,3) x range(0, 10, 3) x.set_step(2) range(0, 10, 2)
Now I'm not saying we *should* allow you to change the step value but we *could*. And if we did that then, the original stop value definitely matters. Making the decision now to ignore that value when comparing for equality means that we preclude other features in the future. --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com

We've been bikeshedding long enough. I propose to do the following to range() in Python 3.3: - add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop And no more. -- --Guido van Rossum (python.org/~guido)

Guido van Rossum schrieb am Fr, 14. Okt 2011, um 10:23:10 -0700:
- add slicing such that it normalizes .stop to .start + the right multiple of .step
That's what slicing already does now. This kind of normalisation still isn't enough to get an implementation of the sequence-based definition of equality, though. We would also need to set the step value to 1 in case the range has length 0 or 1. (Not that I'd propose to do the latter -- I mention it just to make clear that the steps you suggest don't allow for comparison of ranges as sequences in any easier way than currently possible.) Cheers, Sven

On Fri, Oct 14, 2011 at 11:16 AM, Sven Marnach <sven@marnach.net> wrote:
Guido van Rossum schrieb am Fr, 14. Okt 2011, um 10:23:10 -0700:
- add slicing such that it normalizes .stop to .start + the right multiple of .step
That's what slicing already does now.
I guess I tested with an old version of Python 3...
This kind of normalisation still isn't enough to get an implementation of the sequence-based definition of equality, though. We would also need to set the step value to 1 in case the range has length 0 or 1. (Not that I'd propose to do the latter -- I mention it just to make clear that the steps you suggest don't allow for comparison of ranges as sequences in any easier way than currently possible.)
Ok, so we don't have anything to do for slices. It doesn't change my opinion on the rest. -- --Guido van Rossum (python.org/~guido)

On Fri, Oct 14, 2011 at 1:23 PM, Guido van Rossum <guido@python.org> wrote: ..
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
-1 I did not see a clear statement of a use-case for any of these features. I could imagine convenience of __eq__ for those used to range() returning a list, but comparing by .start, .step, .stop would destroy this convenience. If you need an object with .start, .step, .stop, we already have the slice object. NumPy has some functionality to create a regular sequence from a slice object. I don't see why someone would need a __hash__. If you want to key some values by ranges, just use 3-tuples instead.

On Sat, Oct 15, 2011 at 5:52 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Fri, Oct 14, 2011 at 1:23 PM, Guido van Rossum <guido@python.org> wrote: ..
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
-1
I did not see a clear statement of a use-case for any of these features. I could imagine convenience of __eq__ for those used to range() returning a list, but comparing by .start, .step, .stop would destroy this convenience. If you need an object with .start, .step, .stop, we already have the slice object. NumPy has some functionality to create a regular sequence from a slice object. I don't see why someone would need a __hash__. If you want to key some values by ranges, just use 3-tuples instead.
The key point here is that you can *already* invoke '==' and 'hash()' on 3.x ranges - they just have useless identity based semantics. The proposal is merely to make the semantics less pointless for something you can already do. It's also a potential step in the ongoing evolution of ranges towards being more like an optimised tuple of integers (but see my final comment to Guido below). The question is how to define the equivalence classes. There are 3 possible sets of equivalence classes available. In order of increasing size, they are: 1. Identity based (status quo): each range object is equal only to itself 2. Definition based: range objects are equal if their start, stop and step values are equal 3. Behaviour based: range objects are equal if they produce the same sequence of values when iterated over Definitions 2 and 3 produce identical equivalence classes for all non-empty sequences with a step value of 1 (or -1). They only diverge when the sequence is empty or the magnitude of the step value exceeds 1. Under definition 3, all empty ranges form an equivalence class, so "range(1, 1) == range(2, 2)", just like "(0, 1, 2)[1:1] == (0, 1, 2)[2:2]". Under definition 2, the start/stop/step values matter. Under definition 3, all ranges that produces the same output (e.g. just their start value) form an equivalence class, so "range(1, 2, 2) == range(1, 0, -2)" just like "(0, 1, 2)[1:2:2] == (0, 1, 2)[1:0:-2]". As with empty ranges, under definition 2, the start/stop/step values matter. I'll note that under definition 3 (but with start/stop/step exposed), it is easy and intuitive to implement definition 2 semantics: "lhs.start, lhs,stop, lhs.step == rhs.start, rhs.stop, rhs.step" By contrast, under definition 2, implementing definition 3 requires the same contortions as it does now: "len(lhs) == len(rhs) and lhs[0:1] == rhs[0:1] and lhs[-1:] == rhs[-1:]" Guido, I know you wanted to kill this discussion by declaring that definition 2 was the way to go, but I *like* the fact that we've been moving towards a "memory efficient tuple of regularly spaced integers" interaction model for 3.x range objects, and comparison semantics based on exact start/stop/step values would be a definitive break from that model. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Oct 15, 2011 3:08 AM, "Nick Coghlan" <ncoghlan@gmail.com> wrote:
On Sat, Oct 15, 2011 at 5:52 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Fri, Oct 14, 2011 at 1:23 PM, Guido van Rossum <guido@python.org>
wrote:
..
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
-1
I did not see a clear statement of a use-case for any of these features. I could imagine convenience of __eq__ for those used to range() returning a list, but comparing by .start, .step, .stop would destroy this convenience. If you need an object with .start, .step, .stop, we already have the slice object. NumPy has some functionality to create a regular sequence from a slice object. I don't see why someone would need a __hash__. If you want to key some values by ranges, just use 3-tuples instead.
The key point here is that you can *already* invoke '==' and 'hash()' on 3.x ranges - they just have useless identity based semantics. The proposal is merely to make the semantics less pointless for something you can already do.
It's also a potential step in the ongoing evolution of ranges towards being more like an optimised tuple of integers (but see my final comment to Guido below).
The question is how to define the equivalence classes. There are 3 possible sets of equivalence classes available. In order of increasing size, they are:
1. Identity based (status quo): each range object is equal only to itself 2. Definition based: range objects are equal if their start, stop and step values are equal 3. Behaviour based: range objects are equal if they produce the same sequence of values when iterated over
Option 4 would be to kill the __eq__ method for range objects. I think 3 or 4 are the best alternatives. Since we still haven't seen any use cases, option #4 seems like the bikeshed stopper. --Yuval

I have a use case now: switching slice.indices() to return a range object instead of a tuple. That heavily favours the 'behave like a sequence' approach. -- Nick Coghlan (via Gmail on Android, so likely to be more terse than usual) On Oct 15, 2011 8:58 PM, "Yuval Greenfield" <ubershmekel@gmail.com> wrote:
On Oct 15, 2011 3:08 AM, "Nick Coghlan" <ncoghlan@gmail.com> wrote:
On Sat, Oct 15, 2011 at 5:52 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Fri, Oct 14, 2011 at 1:23 PM, Guido van Rossum <guido@python.org>
wrote:
..
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
-1
I did not see a clear statement of a use-case for any of these features. I could imagine convenience of __eq__ for those used to range() returning a list, but comparing by .start, .step, .stop would destroy this convenience. If you need an object with .start, .step, .stop, we already have the slice object. NumPy has some functionality to create a regular sequence from a slice object. I don't see why someone would need a __hash__. If you want to key some values by ranges, just use 3-tuples instead.
The key point here is that you can *already* invoke '==' and 'hash()' on 3.x ranges - they just have useless identity based semantics. The proposal is merely to make the semantics less pointless for something you can already do.
It's also a potential step in the ongoing evolution of ranges towards being more like an optimised tuple of integers (but see my final comment to Guido below).
The question is how to define the equivalence classes. There are 3 possible sets of equivalence classes available. In order of increasing size, they are:
1. Identity based (status quo): each range object is equal only to itself 2. Definition based: range objects are equal if their start, stop and step values are equal 3. Behaviour based: range objects are equal if they produce the same sequence of values when iterated over
Option 4 would be to kill the __eq__ method for range objects.
I think 3 or 4 are the best alternatives. Since we still haven't seen any use cases, option #4 seems like the bikeshed stopper.
--Yuval

On Sat, Oct 15, 2011 at 10:28 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I have a use case now: switching slice.indices() to return a range object instead of a tuple. That heavily favours the 'behave like a sequence' approach.
I like the idea, but why is this a use-case for range.__eq__ or range.__hash__? I like the idea not because it will lead to any optimisation. Slice.indices() does not return a tuple containing all indices in a slice. The result is always a 3-tuple containing normalized start, stop, and step. A range object cannot be more efficient than a 3-tuple. I still like the idea because it would make indices() return what the name suggests - the sequence of indices selectable by the slice.

On Sun, Oct 16, 2011 at 3:21 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Sat, Oct 15, 2011 at 10:28 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I have a use case now: switching slice.indices() to return a range object instead of a tuple. That heavily favours the 'behave like a sequence' approach.
I like the idea, but why is this a use-case for range.__eq__ or range.__hash__?
I like the idea not because it will lead to any optimisation. Slice.indices() does not return a tuple containing all indices in a slice. The result is always a 3-tuple containing normalized start, stop, and step. A range object cannot be more efficient than a 3-tuple.
I still like the idea because it would make indices() return what the name suggests - the sequence of indices selectable by the slice.
Ah, you're quite correct - it only seemed like a use case for equality because I was thinking slice.indices() returned an actual tuple of indices, in which case range() would need to behave like a tuple of integers for compatibility reasons. I forgot that the expression to get the actual indices (rather than the start/stop/step values) is "range(*slice_obj.indices(len(container))". Now that we have full memory efficient range objects, perhaps slice objects should grow a more direct API, along the lines of "slice_obj.make_range(len(container))" Still, "act roughly like a memory efficient tuple of integers" remains a useful design guideline for 3.x range behaviour. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sat, Oct 15, 2011 at 12:04 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Sat, Oct 15, 2011 at 5:52 AM, Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
On Fri, Oct 14, 2011 at 1:23 PM, Guido van Rossum <guido@python.org> wrote: ..
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
-1
I did not see a clear statement of a use-case for any of these features. I could imagine convenience of __eq__ for those used to range() returning a list, but comparing by .start, .step, .stop would destroy this convenience. If you need an object with .start, .step, .stop, we already have the slice object. NumPy has some functionality to create a regular sequence from a slice object. I don't see why someone would need a __hash__. If you want to key some values by ranges, just use 3-tuples instead.
The key point here is that you can *already* invoke '==' and 'hash()' on 3.x ranges - they just have useless identity based semantics. The proposal is merely to make the semantics less pointless for something you can already do.
It's also a potential step in the ongoing evolution of ranges towards being more like an optimised tuple of integers (but see my final comment to Guido below).
The question is how to define the equivalence classes. There are 3 possible sets of equivalence classes available. In order of increasing size, they are:
1. Identity based (status quo): each range object is equal only to itself 2. Definition based: range objects are equal if their start, stop and step values are equal 3. Behaviour based: range objects are equal if they produce the same sequence of values when iterated over
Definitions 2 and 3 produce identical equivalence classes for all non-empty sequences with a step value of 1 (or -1). They only diverge when the sequence is empty or the magnitude of the step value exceeds 1.
Under definition 3, all empty ranges form an equivalence class, so "range(1, 1) == range(2, 2)", just like "(0, 1, 2)[1:1] == (0, 1, 2)[2:2]". Under definition 2, the start/stop/step values matter.
Under definition 3, all ranges that produces the same output (e.g. just their start value) form an equivalence class, so "range(1, 2, 2) == range(1, 0, -2)" just like "(0, 1, 2)[1:2:2] == (0, 1, 2)[1:0:-2]". As with empty ranges, under definition 2, the start/stop/step values matter.
I'll note that under definition 3 (but with start/stop/step exposed), it is easy and intuitive to implement definition 2 semantics: "lhs.start, lhs,stop, lhs.step == rhs.start, rhs.stop, rhs.step"
By contrast, under definition 2, implementing definition 3 requires the same contortions as it does now: "len(lhs) == len(rhs) and lhs[0:1] == rhs[0:1] and lhs[-1:] == rhs[-1:]"
Guido, I know you wanted to kill this discussion by declaring that definition 2 was the way to go, but I *like* the fact that we've been moving towards a "memory efficient tuple of regularly spaced integers" interaction model for 3.x range objects, and comparison semantics based on exact start/stop/step values would be a definitive break from that model.
Ok, you've convinced me on range() equality. If I want to compare the start/stop/step triple I can just extract those values and compare those. I remember in the past thinking about unifying slice() and range() and I couldn't do it. I still can't. I think they should remain separate. -- --Guido van Rossum (python.org/~guido)

On Sat, Oct 15, 2011 at 12:47 PM, Guido van Rossum <guido@python.org> wrote: ..
I remember in the past thinking about unifying slice() and range() and I couldn't do it. I still can't. I think they should remain separate.
One of the issues with slices is that they are deliberately made unhashable to prevent slicing of dictionaries. I am not sure range() objects need to be hashable. To me they are more like *lists* of equally spaced integers rather than *tuples*. (My reasons are not strong, but FWIW they are: (1) tuples are usually containers of heterogeneous objects and regular sequences are lists; and (2) 2.x range() (not xrange()) returns a list rather than a tuple.) On the other hand, making range() objects hashable will put an end to requests for writable .start, .stop, .step.

Alexander Belopolsky wrote:
(1) tuples are usually containers of heterogeneous objects and regular sequences are lists; and (2) 2.x range() (not xrange()) returns a list rather than a tuple.)
But, conceptually, hashability has nothing to do with the homogeneous/heterogeneous distinction. The fact that tuples conflate them is a historical oddity. -- Greg

On Oct 16, 3:04 am, Alexander Belopolsky <alexander.belopol...@gmail.com> wrote:
On Sat, Oct 15, 2011 at 12:47 PM, Guido van Rossum <gu...@python.org> wrote: ..
I remember in the past thinking about unifying slice() and range() and I couldn't do it. I still can't. I think they should remain separate.
One of the issues with slices is that they are deliberately made unhashable to prevent slicing of dictionaries.
Could someone point me to an explanation as to why this is the case? Was it purely to avoid confusion? I could easily see myself trying to use slices as keys in a dictionary dispatch.

alex23 wrote:
I could easily see myself trying to use slices as keys in a dictionary dispatch.
I think the idea is that if someone writes x = some_dict[3:7] it's more likely that they're trying to extract part of a dict (which doesn't work) rather than look up an item whose key is slice(3, 7). -- Greg

I've created two tracker issues for further discussion on this topic: http://bugs.python.org/issue13200 http://bugs.python.org/issue13201 Cheers, Sven

Guido van Rossum wrote:
We've been bikeshedding long enough. I propose to do the following to range() in Python 3.3:
- add read-only attributes .start, .step, .stop
+1
- add slicing such that it normalizes .stop to .start + the right multiple of .step
Already in place.
- add __eq__ and __hash__ which compare by .start, .step, .stop
-1 --> lst1 = [x for x in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] if x % 3 == 0] --> lst2 = [x for x in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] if x % 3 == 0] --> lst3 = [x for x in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] if x % 3 == 0] --> lst1 [0, 3, 6, 9] --> lst2 [0, 3, 6, 9] --> lst3 [0, 3, 6, 9] --> lst1 == lst2 == lst3 True A range is a sequence -- why should identical sequences not compare equal? If you have a case where the start, stop, and step do make a difference then that should be the special case where you write your own custom code. Mike Graham wrote:
For equality and comparison, this should be the standard. range objects are sequences, and they should compare just like other sequences. If implemented at all, equality should be that they have the same items in the same order.
+1 ~Ethan~

On Fri, Oct 14, 2011 at 1:10 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
A range is a sequence -- why should identical sequences not compare equal?
There's no such convention anywhere in Python. (1, 2) != [1, 2]. collections/abc.py does not define __eq__ for sequences. Have you personally written code that compares ranges? -- --Guido van Rossum (python.org/~guido)

Guido van Rossum wrote:
On Fri, Oct 14, 2011 at 1:10 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
A range is a sequence -- why should identical sequences not compare equal?
There's no such convention anywhere in Python. (1, 2) != [1, 2]. collections/abc.py does not define __eq__ for sequences.
Okay, add in 'of the same type'.
Have you personally written code that compares ranges?
I have not. Nevertheless, I expect a sequence object of type SomeType that returns the identical items, in the same order, as another sequence object of type SomeType to compare equal to that other sequence object no matter how the two objects happened to be created. ~Ethan~

On 10/14/2011 1:23 PM, Guido van Rossum wrote:
We've been bikeshedding long enough. I propose to do the following to range() in Python 3.3:
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
I have sometimes thought that we should unify slice and range objects by either adding .__iter__ to slice objects or adding the necessary attributes to range objects. The proposal above comes close to doing the latter. I presume that slice.__eq__ does what you propose for range. All that would be missing from range is the slice.indices method. Both range and slice objects represent a virtual subseqeunce of ints with the same three attributes. We use one to explicitly iterate. We use the other to select subsequences with an internal iteration. If range.stop were allowed to be None, as is slice.stop, we also would not need itertools.count, which is the third way we represent a virtual stepped subsequence of ints. -- Terry Jan Reedy

+0 =) On Sat, Oct 15, 2011 at 1:07 PM, Terry Reedy <tjreedy@udel.edu> wrote:
On 10/14/2011 1:23 PM, Guido van Rossum wrote:
We've been bikeshedding long enough. I propose to do the following to range() in Python 3.3:
- add read-only attributes .start, .step, .stop - add slicing such that it normalizes .stop to .start + the right multiple of .step - add __eq__ and __hash__ which compare by .start, .step, .stop
I have sometimes thought that we should unify slice and range objects by either adding .__iter__ to slice objects or adding the necessary attributes to range objects. The proposal above comes close to doing the latter. I presume that slice.__eq__ does what you propose for range. All that would be missing from range is the slice.indices method.
Both range and slice objects represent a virtual subseqeunce of ints with the same three attributes. We use one to explicitly iterate. We use the other to select subsequences with an internal iteration.
If range.stop were allowed to be None, as is slice.stop, we also would not need itertools.count, which is the third way we represent a virtual stepped subsequence of ints.
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

On Sat, Oct 15, 2011 at 12:07 PM, Terry Reedy <tjreedy@udel.edu> wrote:
If range.stop were allowed to be None, as is slice.stop, we also would not need itertools.count, which is the third way we represent a virtual stepped subsequence of ints.
No, you don't want to do that - being finite is an important property of range objects. The thing about slice objects is that they're deliberately incomplete - you need to supply a sequence length in order to "realise" them. This is done via slice.indices(container_len) Now, *there's* a powerful use case in favour of making 3.x range behave just like a tuple of integers: we could update slice.indices() to return one of those instead of wastefully creating the full tuple of indices in memory the way it does now. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (14)
-
alex23
-
Alexander Belopolsky
-
Bruce Leban
-
Daniel Urban
-
David Townshend
-
Ethan Furman
-
Greg Ewing
-
Guido van Rossum
-
Matt Joiner
-
MRAB
-
Nick Coghlan
-
Sven Marnach
-
Terry Reedy
-
Yuval Greenfield