Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thoughts on PEP284

0 views
Skip to first unread message

Stephen Horne

unread,
Sep 22, 2003, 8:51:54 PM9/22/03
to

Some more looping thoughts - this time on integer for loops...

There may be some hint towards PEP284 (integer for loops) in a review
of ideas from other languages, but I'm damned if i can figure it out.
I spent some time thinking about it and couldn't find anything that
would cover the issue.

All I came up with was the recognition that some other languages have
'for' and 'foreach' loops. But Python went down the route of using
'for' for looping over lists/iterators a long time ago. Maybe a new
keyword or combination could help, such as...

for range i in 0 <= i < 10 :

or...

for range 0 <= i < 10 :

or dropping the PEP284 concept in favor of something C based...

for i yield i = 0; i < 10; i += 1 :

but I don't much like any of those, to be honest.


I did come up with something, though...

I doubt the need for exclusive ranges in integer for loops. I also
doubt the need for switching between different range systems
(inclusive, exclusive, half-open). IMO there is more confusion than
anything down those routes.

Although I haven't had problems with inclusive loops (Basic, Ada) my
preference is toward half-open loops if a choice needs to be made. And
it occurs to me that Python programmers do specify integer ranges in a
half-open way on a regular basis - when doing slicing on lists and
strings.

Perhaps the 'int' and 'long' types could support a slicing to generate
a list or iterator over the range?

If so, we could write...

for i in int[0:10] :
...

If the syntax creates an iterator rather than a list, it could even be
used as follows...

for i in long[0:] :
...
break if <condition> :
...

We could have some basic in-step looping with...

for i, j in zip(int[0:], int[len(list)-1::-1] :
...
break if i > l :
...

but this is probably an argument against.

What would be good, however, is that it can be useful outside of
looping for free - list comprehensions, for instance...

[i*i for i in int[0:10]]

It would need some slight restrictions compared with normal slicing
(it needs to know the value to start at, even if not where to stop)
but what the hell. At least I'm not going to be accused of trying to
turn Python into another language - AFAIK this slice-of-a-type idea is
original. This could be my first time being accused of trying to turn
Python into a language that doesn't exist yet, of course ;-)


--
Steve Horne

steve at ninereeds dot fsnet dot co dot uk

Sean Ross

unread,
Sep 22, 2003, 10:41:04 PM9/22/03
to
"Stephen Horne" <$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$.co.uk> wrote in
message news:5j4vmvgnr5v68f6lu...@4ax.com...

>
> Some more looping thoughts - this time on integer for loops...

Hi.
How about the following:

for i in 0...10:
# suite

where 0...10 works something like xrange(10). So you get i=0 then 1 then 2
... then 9. To get 0 thru 10, inclusive, you'd need to say 0...11 (Ruby has
0..10, but I doubt that'd pass muster here). This suggestion,
unfortunately, does not provide step control and I can't think of a clean
way to introduce it other than via keyword:

for i in 0...10 by 2:
# suite

which will also be shot down, I suspect, as would

for i in 0 to 10 by 2:

for i in 0 thru 10 by 2:

etc.

Perhaps generators could grow a by() method so that you could control how
you step thru the iteration, something like this:

for i in 0...10.by(2):
# suite

Whatever.

Leaving the ellipsis aside, you could add methods to int/long (also ala
Ruby),

for i in 0.upto(10):
# suite

# to count down
for i in 10.downto(0):
# suite

where the methods upto() and downto() are generators with an optional step
argument, i.e.

def upto(self, stop, step=1):
# suite


And, after all of that, you could say, well we already have

for i in range(10):
# suite

If only range was a generator function...but that'd break code, and you can
use xrange(), and some day it will(may) be, and ..., and ..., yada, yada,
yada.

Whatever.


Stephen Horne

unread,
Sep 22, 2003, 11:59:32 PM9/22/03
to
On Mon, 22 Sep 2003 22:41:04 -0400, "Sean Ross"
<sr...@connectmail.carleton.ca> wrote:

>"Stephen Horne" <$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$.co.uk> wrote in
>message news:5j4vmvgnr5v68f6lu...@4ax.com...
>>
>> Some more looping thoughts - this time on integer for loops...
>
>Hi.
>How about the following:
>
>for i in 0...10:
> # suite
>
>where 0...10 works something like xrange(10). So you get i=0 then 1 then 2
>... then 9. To get 0 thru 10, inclusive, you'd need to say 0...11 (Ruby has
>0..10, but I doubt that'd pass muster here).

Personally, I think the conventional '..' has more chance than your
'...'. Basically, the idea of '..' specifying an inclusive range is a
familiar idea to most people, even those who haven't used languages
like Ruby or Ada that use that notation. Having a syntax that looks
similar but means something just slightly different is likely to be
seriously error prone and confusing.

I'm certainly not against '..' in the conventional sense (I like
half-open ranges, but I'm not a fundamentalist and inclusive ranges
have worked perfectly well in Basic and Ada (and IIRC Pascal, Modula 2
and others).

Actually, the main reason I didn't mention it myself is that I figure
there must be some reason for its being rejected in the past. I can't
seriously believe that it has never been proposed.


>Leaving the ellipsis aside, you could add methods to int/long (also ala
>Ruby),
>
>for i in 0.upto(10):
> # suite
>
># to count down
>for i in 10.downto(0):
> # suite

I'm not sure about this - I'm bouncing between "that's neat!" and
"yuck!", but the "yuck!" part may just mean I need to get used to it.

David Eppstein

unread,
Sep 23, 2003, 12:00:32 AM9/23/03
to
In article <SnObb.1581$PT3.2...@news20.bellglobal.com>,
"Sean Ross" <sr...@connectmail.carleton.ca> wrote:

> > Some more looping thoughts - this time on integer for loops...
>
> Hi.
> How about the following:
>
> for i in 0...10:
> # suite
>
> where 0...10 works something like xrange(10).

I can't find the message you're replying to, so am responding here only
to yours.

I personally am uninterested in an integer for-loop syntax that is
limited to the unintuitiveness of range and xrange's arguments --
they're great for looping forwards through the indices into a list, not
so great for almost anything else. If that's all the loop syntax can
do, why not just keep the current "for i in range(10):" syntax?
"Explicit is better than implicit."

But anyway, PEP 284 has been kind of moribund since Guido dissed it in
his Parade of PEPs. I'm not sure what (if anything) it would take to
revive it, but convoluted syntax like "for i in 0...10.by(2):" is
probably not it. "for i in 0:10:2:" would be more Pythonic, but has a
little ambiguity problem ("for i in 0:10:2" without the colon seems like
it should execute the statement "2" ten times) and again doesn't add
much in readability or power to what we can already do with range.

--
David Eppstein http://www.ics.uci.edu/~eppstein/
Univ. of California, Irvine, School of Information & Computer Science

M-a-S

unread,
Sep 23, 2003, 12:49:42 AM9/23/03
to

> > Some more looping thoughts - this time on integer for loops...

I guess this should be a matter of optimization. Why don't the Python
compiler recognize 'for <var> in [x]range(<from>,<till>):'? It should be
pretty easy. Microsoft does marvels optimizing loops in C. Why an open
source project like Python can't do it?

M-a-S


Stephen Horne

unread,
Sep 23, 2003, 12:49:14 AM9/23/03
to
On Mon, 22 Sep 2003 21:00:32 -0700, David Eppstein
<epps...@ics.uci.edu> wrote:

>"for i in 0:10:2:" would be more Pythonic, but has a
>little ambiguity problem ("for i in 0:10:2" without the colon seems like
>it should execute the statement "2" ten times) and again doesn't add
>much in readability or power to what we can already do with range.

That's pretty close to my suggestion, which - in brief - was...

for i in int [0:10:2] :
...

that is, allow the 'int' type object to be sliced, and return an
iterator as the result - not just for the for loop, but generally
(though optimising the 'for' loop usage to avoid physically creating
the iterator might be a good idea).

This shouldn't be too wierd as Pythonistas are already familiar with
slicing - it's just that a type is being sliced rather than a list or
string. But to anyone familiar with computer science, a set of values
is a key feature of any abstract data type anyway. It just so happens
that the set of integers is conveniently sliceable (as long as the
start value is specified).

True, it is mostly just an alternative notation for range or xrange.
But the integer for loops thing is something that never seems to go
away. PEP284 itself quotes four previous PEPs on broadly the same
issue.

The rejected PEP204 is uncomfortably close to my (and your)
suggestions, but while the difference is small I think it is
significant. It is reasonable to think of a type as a potentially
sliceable set of values, and practical to implement providing the type
has certain properties such as being discrete.

Also, one extra feature is that the loop can be infinite (which range
and xrange cannot achieve)...

for i in int [0:] :
...
if condition : break
...

Sean Ross

unread,
Sep 23, 2003, 12:44:07 AM9/23/03
to
"David Eppstein" <epps...@ics.uci.edu> wrote in message
news:eppstein-E0F40C...@news.service.uci.edu...

> In article <SnObb.1581$PT3.2...@news20.bellglobal.com>,
> "Sean Ross" <sr...@connectmail.carleton.ca> wrote:
>
> > > Some more looping thoughts - this time on integer for loops...
> >
> > Hi.
> > How about the following:
> >
> > for i in 0...10:
> > # suite
> >
> > where 0...10 works something like xrange(10).
>
> I can't find the message you're replying to, so am responding here only
> to yours.
>
> I personally am uninterested in an integer for-loop syntax that is
> limited to the unintuitiveness of range and xrange's arguments --
> they're great for looping forwards through the indices into a list, not
> so great for almost anything else. If that's all the loop syntax can
> do, why not just keep the current "for i in range(10):" syntax?

Hi.
Right.

Quote from me:


"""
And, after all of that, you could say, well we already have

for i in range(10):
# suite

...
and you can use xrange()
...

yada, yada, yada.

Whatever.
"""


> "Explicit is better than implicit."
>

Um. Actually, for i in 0...10 _is_ explicit. It explicitly asks for the
integers from 0 thru 9, just as xrange(10) does, but with different syntax.

"where 0...10 works something like xrange(10). "

me, again

I'm familiar with seeing ellipsis used to denote ranges (from textbooks,
other programming languages, and general every day writings), so, to me it's
looks explicit. (Although, I would usually read it as the numbers from 0 to
10, inclusive). But, again, whatever.

> But anyway, PEP 284 has been kind of moribund since Guido dissed it in
> his Parade of PEPs. I'm not sure what (if anything) it would take to
> revive it,

Sure. I've no intentions of pursuing this syntax proposal. The PEP was
brought up. A syntax suggestion was made. I didn't much care for those, so I
made others. My interest ends there.

> but convoluted syntax like "for i in 0...10.by(2):" is
> probably not it.

Right.

"This suggestion ... does not provide step control and I can't think of a
clean way to introduce it ..."
me

the by() suggestion was a demonstration of how I could not "think of a clean
way to introduce" step control for the ellipsis based syntax.

> "for i in 0:10:2:" would be more Pythonic, but has a
> little ambiguity problem ("for i in 0:10:2" without the colon seems like
> it should execute the statement "2" ten times) and again doesn't add
> much in readability or power to what we can already do with range.
>

Right.


David Eppstein

unread,
Sep 23, 2003, 12:53:15 AM9/23/03
to
In article <m2eky8d...@mycroft.actrix.gen.nz>,
Paul Foley <s...@below.invalid> wrote:

> > There may be some hint towards PEP284 (integer for loops) in a review
> > of ideas from other languages, but I'm damned if i can figure it out.
> > I spent some time thinking about it and couldn't find anything that
> > would cover the issue.
>
> > All I came up with was the recognition that some other languages have
> > 'for' and 'foreach' loops. But Python went down the route of using
> > 'for' for looping over lists/iterators a long time ago. Maybe a new
> > keyword or combination could help, such as...
>
> > for range i in 0 <= i < 10 :

The idea behind the specific syntax of PEP284 was simply the following
observation: one way of reading "for i in iterator" is that it loops
over the values for which the expression "i in iterator" is true.
What types of expressions other than the "in" operator could be
substituted in the same context and do something useful?

Stephen Horne

unread,
Sep 23, 2003, 1:14:54 AM9/23/03
to

For all I know it does. Complaints about the 'xrange' notation are
normally about the syntax, not the efficiency. For instance, take this
quote from Guidos parade of PEPs...

"""
PEP 284 - Integer for-loops - Eppstein, Ewing
Yet another way to address the fact that some people find

for i in range(10):

too ugly.
"""

The word is "ugly", not "slow".

Sean Ross

unread,
Sep 23, 2003, 1:14:57 AM9/23/03
to
"Stephen Horne" <$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$.co.uk> wrote in
message
> Personally, I think the conventional '..' has more chance than your
> '...'.

Hi.
Actually, I think '...' is more conventional notation for expressing a range
of numbers. Like when you want to show a sequence:

# odd numbers greater than 3, but less than 257
5, 7, 9, 11, ..., 255, 257

That sort of thing.

Also, '...' is one dot further away from a single dot '.', than is '..'. Is
that important? Maybe. I figure it takes away one argument:

"a..b looks too much like a.b"

by saying

"Well, a...b doesn't"

Of course, a#%$^^#b doesn't either....

Anyway ...


> >Leaving the ellipsis aside, you could add methods to int/long (also ala
> >Ruby),
> >
> >for i in 0.upto(10):
> > # suite
> >
> ># to count down
> >for i in 10.downto(0):
> > # suite
>
> I'm not sure about this - I'm bouncing between "that's neat!" and
> "yuck!", but the "yuck!" part may just mean I need to get used to it.
>

I'm more:
I've seen it in Ruby and Smalltalk (where integers have such methods), and
it's not '...' , nor is it range(), so that might be an alternative. Whether
it is a good alternative ... <shrug>. It doesn't add new syntax, only new
methods to existing objects. But, it does not appear to provide any marked
improvement over range (or xrange). So, it's neither here nor there for me.

In my own language, were I to make one, I would use:

for i in start to stop by step:
# suite

But, that's not going to happen in this language; neither do I expect it to,
nor would I ask for it (I'd make my own, if I really wanted it).
Still, it's interesting to play with different formulations, to see if they
do or do not work well.


Stephen Horne

unread,
Sep 23, 2003, 1:30:07 AM9/23/03
to
On Mon, 22 Sep 2003 21:53:15 -0700, David Eppstein
<epps...@ics.uci.edu> wrote:

>In article <m2eky8d...@mycroft.actrix.gen.nz>,
> Paul Foley <s...@below.invalid> wrote:
>
>> > There may be some hint towards PEP284 (integer for loops) in a review
>> > of ideas from other languages, but I'm damned if i can figure it out.
>> > I spent some time thinking about it and couldn't find anything that
>> > would cover the issue.
>>
>> > All I came up with was the recognition that some other languages have
>> > 'for' and 'foreach' loops. But Python went down the route of using
>> > 'for' for looping over lists/iterators a long time ago. Maybe a new
>> > keyword or combination could help, such as...
>>
>> > for range i in 0 <= i < 10 :
>
>The idea behind the specific syntax of PEP284 was simply the following
>observation: one way of reading "for i in iterator" is that it loops
>over the values for which the expression "i in iterator" is true.
>What types of expressions other than the "in" operator could be
>substituted in the same context and do something useful?

OK - that makes sense, but I don't think I'd naturally read '0 <= i <
10' as an iterator without a good deal of prompting. Even with the
explanation, I still feel the need for a different (or extra) keyword
to emphasise the difference.

Raymond Hettinger

unread,
Sep 23, 2003, 1:51:26 AM9/23/03
to
> I guess this should be a matter of optimization. Why don't the Python
> compiler recognize 'for <var> in [x]range(<from>,<till>):'? It should be
> pretty easy. Microsoft does marvels optimizing loops in C. Why an open
> source project like Python can't do it?

Yes, it's true that C's for(i=0;i<n;i++){action} runs faster than Python's
for i in xrange(n). However, once you've converted "i" to a PyInt
object and done signal checking / thread switching on each iteration, the
gap closes quite a bit. Python's for loops are very lightweight and their
overhead tends to be tiny in comparison to any real work being done
inside the loop.

To get true marvels, you need to do something like psyco and be able
dynamically analyze the loop to determine that "i" need not be a PyInt
but can be stored, incremented, and accessed as a C long.

Short of dynamic analysis and native code generation, your best bet
is to look at stack frame creation and argument passing when calling
a function written in pure python. The next best bet is to look at
optimizing method lookup and calls. In comparision, the performance
of the for-loop is a red herring.


Raymond Hettinger


P.S. If you don't need to use "i" in the body of the loop, the high-speed
pure python version becomes: for _ in itertools.repeat(None, n): body

If the loop is boundless, then "while 1" is even better (it has practically
*no* overhead).


Stephen Horne

unread,
Sep 23, 2003, 2:10:50 AM9/23/03
to
On Tue, 23 Sep 2003 01:14:57 -0400, "Sean Ross"
<sr...@connectmail.carleton.ca> wrote:

>"Stephen Horne" <$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$.co.uk> wrote in
>message
>> Personally, I think the conventional '..' has more chance than your
>> '...'.
>
>Hi.
>Actually, I think '...' is more conventional notation for expressing a range
>of numbers. Like when you want to show a sequence:
>
># odd numbers greater than 3, but less than 257
>5, 7, 9, 11, ..., 255, 257
>
>That sort of thing.

Good point, but I think the commas are essential to making that clear.
Without the commas, it simply doesn't look like the same thing.


>I've seen it in Ruby and Smalltalk (where integers have such methods), and
>it's not '...' , nor is it range(), so that might be an alternative. Whether
>it is a good alternative ... <shrug>. It doesn't add new syntax, only new
>methods to existing objects.

That is also an advantage to my slicing idea - it only requires a new
method (__getitem__ IIRC) be implemented for one object (the builtin
type object called 'int').

Daniel Dittmar

unread,
Sep 23, 2003, 5:32:59 AM9/23/03
to
Stephen Horne wrote:
> for i in int [0:10:2] :
> ...
>
> that is, allow the 'int' type object to be sliced, and return an
> iterator as the result - not just for the for loop, but generally
> (though optimising the 'for' loop usage to avoid physically creating
> the iterator might be a good idea).

Adding operators to types is always problematic because it defeats Pythons's
runtime checking. Assuming that integer slicing would be added to Python,
methods that would expect a list of integers would suddenly also work with
integers. In most cases, they would not work correctly, but you wouldn't get
a meaningful exception.

> Also, one extra feature is that the loop can be infinite (which range
> and xrange cannot achieve)...
>
> for i in int [0:] :
> ...
> if condition : break
> ...

I'm sure that you can think of a generator function that does exactly the
same.

Daniel

Alex Martelli

unread,
Sep 23, 2003, 6:22:50 AM9/23/03
to
Daniel Dittmar wrote:

> Stephen Horne wrote:
>> for i in int [0:10:2] :
>> ...
>>
>> that is, allow the 'int' type object to be sliced, and return an
>> iterator as the result - not just for the for loop, but generally
>> (though optimising the 'for' loop usage to avoid physically creating
>> the iterator might be a good idea).
>
> Adding operators to types is always problematic because it defeats
> Pythons's runtime checking. Assuming that integer slicing would be added
> to Python, methods that would expect a list of integers would suddenly
> also work with integers. In most cases, they would not work correctly, but
> you wouldn't get a meaningful exception.

Careful: in Stephen's proposal, slicing would NOT work on *INTEGERS* --
rather, it would work on the *INT TYPE ITSELF*, which is a very, very
different issue. "Methods that would expect a list of integers", ONLY
do slicing on that list, AND get mistakenly passed the type object
'int' itself -- would work just perfectly, as if they had been passed
"a list of all non-negative integers" or xrange(sys.maxint+1).


>> Also, one extra feature is that the loop can be infinite (which range
>> and xrange cannot achieve)...
>>
>> for i in int [0:] :

No way, Jose -- now THAT would break things (in admittedly rare
cases -- a method expecting a list and NOT providing an upper bound
in the slicing). I'd vote for this to be like int[0:sys.maxint+1]
(i.e., last item returned is sys.maxint).

>> ...
>> if condition : break
>> ...
>
> I'm sure that you can think of a generator function that does exactly the
> same.

Right, that's pretty trivial, and itertools.count() already does it anyway.


What Stephen's proposal lacks is rigorous specs of what happens for all
possible slices -- e.g int[0:-3] isn't immediately intuitive, IMHO;-).


Alex

Gerrit Holl

unread,
Sep 23, 2003, 7:16:20 AM9/23/03
to
Alex Martelli wrote:
> >> Also, one extra feature is that the loop can be infinite (which range
> >> and xrange cannot achieve)...
> >>
> >> for i in int [0:] :
>
> No way, Jose -- now THAT would break things (in admittedly rare
> cases -- a method expecting a list and NOT providing an upper bound
> in the slicing). I'd vote for this to be like int[0:sys.maxint+1]
> (i.e., last item returned is sys.maxint).

This one breaks the int/long unification. In an ideal unification
of ints and longs, sys.maxint would not exist any more, and the
upper bound would be seemingly random.

Gerrit.

--
131. If a man bring a charge against one's wife, but she is not
surprised with another man, she must take an oath and then may return to
her house.
-- 1780 BC, Hammurabi, Code of Law
--
Asperger Syndroom - een persoonlijke benadering:
http://people.nl.linux.org/~gerrit/
Het zijn tijden om je zelf met politiek te bemoeien:
http://www.sp.nl/

Alex Martelli

unread,
Sep 23, 2003, 10:29:44 AM9/23/03
to
Gerrit Holl wrote:

> Alex Martelli wrote:
>> >> Also, one extra feature is that the loop can be infinite (which range
>> >> and xrange cannot achieve)...
>> >>
>> >> for i in int [0:] :
>>
>> No way, Jose -- now THAT would break things (in admittedly rare
>> cases -- a method expecting a list and NOT providing an upper bound
>> in the slicing). I'd vote for this to be like int[0:sys.maxint+1]
>> (i.e., last item returned is sys.maxint).
>
> This one breaks the int/long unification. In an ideal unification
> of ints and longs, sys.maxint would not exist any more, and the
> upper bound would be seemingly random.

If this is the "party line" -- that not providing an upper bound
when indexing int should generate an infinitely looping iterator --
then I think the risk of nasty bugs would probably be too high, and
I would reluctantly -1 the otherwise nice proposal. I'd rather
have the missing upper bound be an error in this case (and rely on
itertools.count for very explicit building of infinitely looping
iterators) than "easily create infinities" in response to typos;-).


Alex

Sean Ross

unread,
Sep 23, 2003, 10:40:30 AM9/23/03
to
Here's a quick hack of an int class that supports iteration using the slice
notation, plus simple iteration on the class itself (for i in int: ...).
This can certainly be improved, and other issues need to be addressed, but I
just wanted to see what Stephen's idea would look like in practice. (Not
bad. Actually, pretty good.)

# using python 2.2.2
from __future__ import generators

class xint(int):
class __metaclass__(type):
def __iter__(cls):
return cls.__getitem__()
def __getitem__(cls, index=None):
if hasattr(index, "start"):
for i in range(index.start, index.stop, index.step):
yield i
elif isinstance(index, int):
yield index
elif index is None:
i = 0
while True:
yield i
i += 1
else:
raise Exception
__getitem__ = classmethod(__getitem__)

print "iteration on int"
for i in xint:
if i >= 10:
break
print i

print "\niteration on int slice"
for i in xint[0:22:2]:
print i

#
# OUTPUT
#
iteration on int
0
1
2
3
4
5
6
7
8
9

iteration on int slice
0
2
4
6
8
10
12
14
16
18
20

Tim Hochberg

unread,
Sep 23, 2003, 11:44:13 AM9/23/03
to

FWIW, Psyco recognizes this structure and removes the overhead
associated with it.

-tim

Stephen Horne

unread,
Sep 23, 2003, 12:14:12 PM9/23/03
to
On Tue, 23 Sep 2003 10:22:50 GMT, Alex Martelli <al...@aleax.it>
wrote:

>> Adding operators to types is always problematic because it defeats
>> Pythons's runtime checking. Assuming that integer slicing would be added
>> to Python, methods that would expect a list of integers would suddenly
>> also work with integers. In most cases, they would not work correctly, but
>> you wouldn't get a meaningful exception.
>
>Careful: in Stephen's proposal, slicing would NOT work on *INTEGERS* --
>rather, it would work on the *INT TYPE ITSELF*, which is a very, very
>different issue.

Exactly - thanks for that.

Actually, Sean Ross posted a prototype implementation which makes a
good point - theres no reason why the sliceable object needs to be the
standard 'int' type in Python as it is now. Maybe a library extended
int type, imported only if used, makes sense. Maybe this is a recipe
or library proposal rather than a language proposal.

>>> Also, one extra feature is that the loop can be infinite (which range
>>> and xrange cannot achieve)...
>>>
>>> for i in int [0:] :
>
>No way, Jose -- now THAT would break things (in admittedly rare
>cases -- a method expecting a list and NOT providing an upper bound
>in the slicing). I'd vote for this to be like int[0:sys.maxint+1]
>(i.e., last item returned is sys.maxint).

True - but boundless generators are already in use.

An implicit sys.maxint(ish) upper bound seems wrong to me. I would
either go with allowing the infinite loop or requiring an explicit
upper bound.

>What Stephen's proposal lacks is rigorous specs of what happens for all
>possible slices -- e.g int[0:-3] isn't immediately intuitive, IMHO;-).

You're right in that this was not intended as a full formal proposal.
But lets see what I can do...

To me, it should be possible to create slices for negative ranges as
easily as positive ranges, so the convention of -1 giving the last
item wouldn't apply.

I already had to look at this issue when I wrapped some C++ container
classes for Python recently (unreleased at present due to the need for
further Pythonicising) - the whole point of doing so was that the
containers allow some more power and flexibility compared with the
Python ones. For instance, my set and (dictionary-like) map classes
are conveniently and efficiently sliceable. But how should the slicing
work?

BTW - there is a price for flexibility - I was very surprised at the
relative speed of a dictionary (at least an order of magnitude faster
than my map) though to be fair I haven't done even trivial
optimisation stuff yet. No, these aren't STL containers.

Anyway, back to the point...

I could have treated slices as specifying subscripts (while the data
structure is *not* a sorted array, it does support reasonably
efficient subscripting) but I felt the upper and lower bounds should
be key values rather than subscripts. But when basing the bounds on
keys, a bound of '-1' logically means a bound with the key value of -1
- not the highest subscript value.

The 'step' value is slightly inconsistent in that it had to be a
subscript-like step (stepping over a specific number of items rather
than a specific key range) as the keys don't always have a sensible
way to interpret these steps. But I'm drifting from the point again.

As a 'set of integers' would IMO logically be sliced by key too, '-1'
should just be a key like any other key, giving...

>>> int [-5:0]
[-5, -4, -3, -2, -1]

>>> int [0:-5]
[]

>>> int [0:-5:-1]
[0, -1, -2, -3, -4]

So to me, the slice should be evaluated as follows...

if step is None : default step to 1
if step == 0 : raise IndexError

if start is None :
either default to zero or raise IndexError, not sure

if stop is None :
either raise IndexError or...

if step > 0 :
default stop to +infinity
else :
default stop to -infinity

Obviously that isn't a real implementation ;-)

Stephen Horne

unread,
Sep 23, 2003, 12:19:12 PM9/23/03
to
On Tue, 23 Sep 2003 08:44:13 -0700, Tim Hochberg
<tim.ho...@ieee.org> wrote:

>FWIW, Psyco recognizes this structure and removes the overhead
>associated with it.

I went looking for Psyco yesterday, and all I could find was broken
links. Was sourceforge just having bad day, or is there a new site
that hasn't made it into Google yet?

Stephen Horne

unread,
Sep 23, 2003, 12:27:13 PM9/23/03
to
On Tue, 23 Sep 2003 10:40:30 -0400, "Sean Ross"
<sr...@connectmail.carleton.ca> wrote:

>Here's a quick hack of an int class that supports iteration using the slice
>notation, plus simple iteration on the class itself (for i in int: ...).
>This can certainly be improved, and other issues need to be addressed, but I
>just wanted to see what Stephen's idea would look like in practice. (Not
>bad. Actually, pretty good.)

I like it ;-)

Also, it kind of suggests that maybe a recipe or a library 'xint'
class or C extension module could do the job as well as a language
change.

Stephen Horne

unread,
Sep 23, 2003, 12:31:08 PM9/23/03
to
On Tue, 23 Sep 2003 14:29:44 GMT, Alex Martelli <al...@aleax.it>
wrote:

>I'd rather


>have the missing upper bound be an error in this case (and rely on
>itertools.count for very explicit building of infinitely looping
>iterators) than "easily create infinities" in response to typos;-).

I probably agree - convenience is nice, but easy-to-make errors are
somewhat less nice.

A compulsory upper bound is probably a good idea. Though maybe
explicitly recognising the strings "+inf" and "-inf" in slice.stop
would be reasonable?

Michael Chermside

unread,
Sep 23, 2003, 2:31:03 PM9/23/03
to
Stephen Horne writes:
> True, it is mostly just an alternative notation for range or xrange.
> But the integer for loops thing is something that never seems to go
> away. PEP284 itself quotes four previous PEPs on broadly the same
> issue.

I am of the opinion that the introduction of enumerate() (in 2.3) will
go a long ways toward reducing the desire for better integer loop
syntax. I'm sure it will never go away completely, but if the demand
seems to die down after a year or so, then I'd guess it was due to
enumerate().

-- Michael Chermside


Tim Hochberg

unread,
Sep 23, 2003, 3:08:04 PM9/23/03
to
Stephen Horne wrote:
> On Tue, 23 Sep 2003 08:44:13 -0700, Tim Hochberg
> <tim.ho...@ieee.org> wrote:
>
>
>>FWIW, Psyco recognizes this structure and removes the overhead
>>associated with it.
>
>
> I went looking for Psyco yesterday, and all I could find was broken
> links. Was sourceforge just having bad day, or is there a new site
> that hasn't made it into Google yet?


Probably the former. I just checked at http://psyco.sourceforge.net/ and
everything seemed to be working.

-tim

David Eppstein

unread,
Sep 23, 2003, 3:26:56 PM9/23/03
to
In article <mailman.1064341934...@python.org>,
Michael Chermside <mch...@mcherm.com> wrote:

It will certainly reduce the typical range(len(L)) verbosity. It
doesn't do anything to help the readability of expressions like
range(dim-2,-1,-1) (example from some code I wrote a couple weeks ago).

Actually, looking at that code, the range is not the least readable part
of it:

if dim == 1:
positions = dict([(s, (positions[s][0],0)) for s in positions])
elif dim == 2:
positions = dict([(s, (2*positions[s][0],2*positions[s][1]))
for s in positions])
elif not place3d():
X = coordinate(range(1,dim))
Y = coordinate(range(dim-2,-1,-1))
positions = dict([(s, (X[s],Y[s])) for s in positions])

Dictionary comprehensions (PEP 274) would go a long way towards cleaning
this up:

if dim == 1:
positions = {s: (positions[s][0],0) for s in positions}
elif dim == 2:
positions = {s: (2*positions[s][0],2*positions[s][1]))
for s in positions}
elif not place3d():
X = coordinate(range(1,dim))
Y = coordinate(range(dim-2,-1,-1))
positions = {s: (X[s],Y[s]) for s in positions}

The use of PEP 284 syntax (replace the range by [i for dim-2 >= i >= 0])
would I think help but not as much.

Stephen Horne

unread,
Sep 23, 2003, 3:24:52 PM9/23/03
to
On Tue, 23 Sep 2003 12:08:04 -0700, Tim Hochberg
<tim.ho...@ieee.org> wrote:

>Stephen Horne wrote:
>> On Tue, 23 Sep 2003 08:44:13 -0700, Tim Hochberg
>> <tim.ho...@ieee.org> wrote:
>>
>>
>>>FWIW, Psyco recognizes this structure and removes the overhead
>>>associated with it.
>>
>>
>> I went looking for Psyco yesterday, and all I could find was broken
>> links. Was sourceforge just having bad day, or is there a new site
>> that hasn't made it into Google yet?
>
>
>Probably the former. I just checked at http://psyco.sourceforge.net/ and
>everything seemed to be working.

You're right - I can see it now. Thanks.

Tim Hochberg

unread,
Sep 23, 2003, 3:43:34 PM9/23/03
to
David Eppstein wrote:

> In article <mailman.1064341934...@python.org>,
> Michael Chermside <mch...@mcherm.com> wrote:
>
>
>>Stephen Horne writes:
>>
>>>True, it is mostly just an alternative notation for range or xrange.
>>>But the integer for loops thing is something that never seems to go
>>>away. PEP284 itself quotes four previous PEPs on broadly the same
>>>issue.
>>
>>I am of the opinion that the introduction of enumerate() (in 2.3) will
>>go a long ways toward reducing the desire for better integer loop
>>syntax. I'm sure it will never go away completely, but if the demand
>>seems to die down after a year or so, then I'd guess it was due to
>>enumerate().
>
>
> It will certainly reduce the typical range(len(L)) verbosity. It
> doesn't do anything to help the readability of expressions like
> range(dim-2,-1,-1) (example from some code I wrote a couple weeks ago).

Dealing with reversed ranges is one area that range bugs me. However, in
2.3 you can replace ``range(n-1,-1,-1)`` with ``range(n)[::-1]``, which
while still verbose is a lot easier for me to interpret. [Now if I could
just convinve Armin Rigo to special case that in Psyco so that it was as
fast as the original...]

I know someone's going to suggest that, since I'm already using slices,
I should be in favor of int[a:b:c] or similar. But, that doesn't work
any better than range with reversed ranges since you still have to use
two slices to make things clear. That is, to get the equivalent of
``range(n-1,-1,-1)`` one would need to use ``int[n-1:-1:-1]``, which is
no clearer, or ``int[:n][::-1]`` which doesn't seem like a particular
win over ``range(n)[::-1]``, which you can do now.


> Actually, looking at that code, the range is not the least readable part
> of it:

[Snip dictionary comprehension stuff]

-tim

Stephen Horne

unread,
Sep 23, 2003, 5:03:04 PM9/23/03
to
On Tue, 23 Sep 2003 12:43:34 -0700, Tim Hochberg
<tim.ho...@ieee.org> wrote:

>I know someone's going to suggest that, since I'm already using slices,
>I should be in favor of int[a:b:c] or similar. But, that doesn't work
>any better than range with reversed ranges since you still have to use
>two slices to make things clear. That is, to get the equivalent of
>``range(n-1,-1,-1)`` one would need to use ``int[n-1:-1:-1]``, which is
>no clearer, or ``int[:n][::-1]`` which doesn't seem like a particular
>win over ``range(n)[::-1]``, which you can do now.

One option is, of course...

for i in int[:n] :
j = n - i - 1
...

or...

for i in int[1:n+1] :
j = n - i
...


And I thought half-open ranges were supposed to eliminate all those +1
and -1 things!


I mentioned before that I have recently coded an extension module with
some containers, and had to deal with some slicing issues. One issue
which I haven't resolved yet is whether to add some sliceable
properties for alternate slicing methods (mainly slicing by subscript
on associative containers that are by default sliced by key).

Running with this idea, how about...

for i in int.backward [:n] :
...

where 'backward' means you get the same slice, but iterated in reverse
order. Actually, I'd vote for a 'backward' sliceable property in
strings, lists and tuples too (though the name should probably be
changed). I might even make the property give a more general proxy for
the object that behaves as if the items were reversed (though maybe
not - the number of methods to handle for string in particular would
be a nightmare).


A reverse function (not the existing reverse methods which work
in-place) would of course handle most cases perfectly well. Why can't
we have a built-in library function doing something like...

def reverse(x) :
y = x [:]
y.reverse ()
return y

Trivial, yes - but probably very useful if you ask me. And I swear it
used to exist, though perhaps only for strings (before some string
stuff got converted to methods).


Anyway, I really don't think this 'backward' property should be part
of the standard int type - I'm rapidly getting more into the idea of
taking Seans prototype from here...

Message-ID: <lWYbb.4739$yD1.6...@news20.bellglobal.com>

removing the inheritance from int (which is probably redundant) and
making it purely into a library object or recipe. It may be time for
me to properly figure out metatypes ;-)

Raymond Hettinger

unread,
Sep 23, 2003, 7:28:43 PM9/23/03
to
[Stephen Horne]

> Running with this idea, how about...
>
> for i in int.backward [:n] :
> ...


I would support adding a iter_backwards() method to
strings, lists, and xrange objects.

for i in xrange(n).iter_backwards():
print i

for i in range(n).iter_backwards():
print i

for c in "flog".iter_backwards():
print c

It could also be implemented for other sequence objects
if there were compelling use cases and if the implementation
wasn't too hairy (file objects for instance).


Raymond Hettinger


Stephen Horne

unread,
Sep 23, 2003, 7:28:44 PM9/23/03
to
On Tue, 23 Sep 2003 22:03:04 +0100, Stephen Horne
<$$$$$$$$$$$$$$$$$@$$$$$$$$$$$$$$$$$$$$.co.uk> wrote:

>On Tue, 23 Sep 2003 12:43:34 -0700, Tim Hochberg
><tim.ho...@ieee.org> wrote:
>
>>I know someone's going to suggest that, since I'm already using slices,
>>I should be in favor of int[a:b:c] or similar. But, that doesn't work
>>any better than range with reversed ranges since you still have to use
>>two slices to make things clear. That is, to get the equivalent of
>>``range(n-1,-1,-1)`` one would need to use ``int[n-1:-1:-1]``, which is
>>no clearer, or ``int[:n][::-1]`` which doesn't seem like a particular
>>win over ``range(n)[::-1]``, which you can do now.

...

>Running with this idea, how about...
>
> for i in int.backward [:n] :
> ...

...

>Anyway, I really don't think this 'backward' property should be part
>of the standard int type - I'm rapidly getting more into the idea of
>taking Seans prototype from here...
>
>Message-ID: <lWYbb.4739$yD1.6...@news20.bellglobal.com>
>
>removing the inheritance from int (which is probably redundant) and
>making it purely into a library object or recipe. It may be time for
>me to properly figure out metatypes ;-)

Well, I read up on metatypes (a bit of an anticlimax there - two pages
of Python in a Nutshell seemed to cover it) but then of course I
realised that if I'm not making a subtype of int I don't need
metatypes. So here we have noddy implementation 1, which I have saved
as "islice.py"...

#################################################################
#
# Library allowing slicing into the 'set of integers'.
#
# The only identifier intended for export is 'xint'.
#
# Slicing of xint results in an equivalent xrange object. The slicing
# is handled as follows...
# start : defaults to zero
# stop : if undefined, raise IndexError
# step : defaults to one
#
# Additionally, the read-only property "backward" returns a similar
# object, also used primarily for slicing. When sliced, it behaves
# exactly as 'xint' *except* that the items in the slice are iterated
# in reverse order.
#
# Note - negative start and stop values do not have any special
# meaning (-1 is not equivalent to the highest integer, for instance)
# because slices including -ve values may be useful in their own
# right.
#
# Example...
#
# from islice import xint
#
# for i in xint [:5] :
# print i, " * 10 is ", i*10
#
# for i in xint.backward [:5] :
# print i, " * 10 is ", i*10
#
# Expected results...
#
# 0 * 10 is 0
# 1 * 10 is 10
# 2 * 10 is 20
# 3 * 10 is 30
# 4 * 10 is 40
# 4 * 10 is 40
# 3 * 10 is 30
# 2 * 10 is 20
# 1 * 10 is 10
# 0 * 10 is 0
#

class Backward (object) :
"""
This class handles reverse-order slicing into the set of integers.
It gives exactly the same slice as AllInts (which is not the same as
simply reversing start and stop and negating step) but iterates the
items in reverse order.

An instance of this class is returned as the result of evaluating
the xint.backward property.
"""

def __getitem__ (self, p) :
if isinstance (p, slice) :
start = p.start or 0
stop = p.stop
step = p.step or 1

if stop is None :
raise IndexError, "Slice stop cannot be defaulted"

if step == 0 :
raise IndexError, "Slice step cannot be zero"

count = ((stop - 1) - start) // step

stop = start + count * step
start -= step

return xrange (stop, start, -step)

else :
raise IndexError, "Non-slice subscripting is not supported"


class AllInts (object) :
"""
This class handles forward-order slicing into the set of integers,
and provides the property "backward" which gives an object that can
be used for reverse-order slicing.

The slice returned is actually an xrange object which iterates the
correct values in the correct order.

This class should not normally be instantiated by users - there is
an instance called 'xint' which should be used instead.
"""

def __getitem__ (self, p) :
if isinstance (p, slice) :
if p.stop is None :
raise IndexError, "Slice stop cannot be defaulted"

if p.step == 0 :
raise IndexError, "Slice step cannot be zero"

return xrange (p.start or 0, p.stop, p.step or 1)

else :
raise IndexError, "Non-slice subscripting is not supported"

def getBackward (self) :
return Backward ()

backward = property(getBackward)


xint = AllInts ()

#################################################################

And some quick tests...

Python 2.3c1 (#44, Jul 18 2003, 14:32:36) [MSC v.1200 32 bit (Intel)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from islice import xint
>>> list(xint[:10])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(xint.backward[:10])
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> list(xint.backward[:10:3])
[9, 6, 3, 0]
>>> list(xint.backward[:10:2])
[8, 6, 4, 2, 0]
>>> list(xint.backward[:10:7])
[7, 0]
>>> list(xint.backward[:10:-7])
[]
>>> list(xint.backward[:-10:-7])
[-7, 0]
>>> list(xint[:-10:-7])
[0, -7]


I'm not entirely happy with the names, and maybe a C extension module
might be more efficient, but in principle I'm quite happy with this.

Andrew Koenig

unread,
Sep 26, 2003, 1:43:11 PM9/26/03
to
Stephen> I doubt the need for exclusive ranges in integer for loops. I
Stephen> also doubt the need for switching between different range
Stephen> systems (inclusive, exclusive, half-open). IMO there is more
Stephen> confusion than anything down those routes.

Really? I would expect a common usage to be:

for 0 <= index < len(list):
do something with list[index]

--
Andrew Koenig, a...@acm.org

David Eppstein

unread,
Sep 26, 2003, 2:10:57 PM9/26/03
to
In article <yu99brt7...@tinker.research.att.com>,
Andrew Koenig <a...@acm.org> wrote:

Isn't that what the new enumerate(list) is for?

Stephen Horne

unread,
Sep 26, 2003, 4:32:07 PM9/26/03
to

We have half-open already. I was commenting on the need for supporting
several *different* schemes. Basically...

I don't see the need to support this case...

for 0 < index < len(list) : # ie exclusive

And think that supporting one or the other of these two would be
sufficient...

for 0 <= index < len(list) : # ie half-open
for 0 <= index <= len(list) : # ie inclusive

And can I think of any languages that support a variety of cases?

C, C++, Java etc support both half-open and inclusive with a simple
change of the continuation condition operator.

The Pascal, Modula 2, Ada etc seem to stick with inclusive IIRC.

These 'limitations' don't really seem to cause a problem, though.


That said, with all this reverse iteration stuff we've been discussing
recently, there is a point to make. If half-open ranges are common,
then the 'reverse' half-open case may be useful too...

for len(list) > index >= 0 :

It's basically a case of symmetry. It avoids the need for all those
'-1' corrections we've been stressing about just recently.


Well, who says I can't have second thoughts.

Still not keen on the syntax, though. And as exclusive ranges have no
apparent frequent use, and rewriting inclusive ranges as half-open
ranges is not really a problem, so really we only need to support the
two half-open cases. And that is really what all the backward
iteration stuff is about.

Alex Martelli

unread,
Sep 27, 2003, 12:17:08 PM9/27/03
to
David Eppstein wrote:
...

>> Really? I would expect a common usage to be:
>>
>> for 0 <= index < len(list):
>> do something with list[index]
>
> Isn't that what the new enumerate(list) is for?

Not necessarily. enumerate is for when you need the values of both index
AND somelist[index], which is clearly a pretty common case. But sometimes
you don't care about the "previous value" of somelist[index]. E.g., say
that your specs are:

if issospecial(index) returns true, then, whatever the previous value
of somelist[index] might have been, it must be replaced with beeble.next().

Then, expressing this as:

for index in range(len(somelist)):
if issospecial(index):
somelist[index] = beeble.next()

looks rather better to me than:

for index, item in enumerate(somelist):
if issospecial(index):
somelist[index] = beeble.next()

where the fact that 'item' is being so deliberately REQUESTED... and
then utterly IGNORED... makes me wonder if the code's author may not
have committed some typo, or something.


Admittedly, these cases where you don't CARE about the previous values
of items ARE rare enough that enumerate use-cases vastly outnumber them.


Alex

0 new messages