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

Arithmetic sequences in Python

4 views
Skip to first unread message

Gregory Petrosyan

unread,
Jan 16, 2006, 4:01:39 AM1/16/06
to
Please visit http://www.python.org/peps/pep-0204.html first.

As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice. So, maybe
there's a reason to adopt slightly modified Haskell's syntax? Something
like

[1,3..10] --> [1,3,5,7,9]
(1,3..10) --> same values as above, but return generator instead of
list
[1..10] --> [1,2,3,4,5,6,7,8,9,10]
(1 ..) --> 'infinite' generator that yield 1,2,3 and so on
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on

So,
1) "[]" means list, "()" means generator
2) the "start" is required, "step" and "end" are optional.

Also, this can be nicely integrated with enumerations (if they will
appear in python). Haskell is also example of such integration.

Bas

unread,
Jan 16, 2006, 4:17:47 AM1/16/06
to
I like the use of the colon as in the PEP better: it is consistant with
the slice notation and also with the colon operator in Matlab.

I like the general idea and I would probably use it a lot if available,
but the functionality is already there with range and irange.

Bas

Paul Rubin

unread,
Jan 16, 2006, 4:26:58 AM1/16/06
to
"Gregory Petrosyan" <gregory....@gmail.com> writes:
> As you can see, PEP 204 was rejected, mostly because of not-so-obvious
> syntax. But IMO the idea behind this pep is very nice. So, maybe
> there's a reason to adopt slightly modified Haskell's syntax?

I like this with some issues: Python loops tend to be 0-based, so
while it's convenient to express the sequence [1..n], what you usually
want is [0..(n-1)] which is uglier.

If you want to count down from f(n) to zero, in Haskell you might say

[b, b-1 .. 0] where b=f(n)

There's no "where" in Python, so what do you do?

[f(n)-1, f(n)-2 .. 0]

evaluates f twice (and might not even get the same result both times),
while the traditional

xrange(f(n)-1, -1, -1)

only evaluates it once but is IMO repulsive.

Anyway I've never liked having to use range or xrange to control
Python loops. It's just kludgy and confusing. So I think your idea
has potential but there's issues that have to be worked out.

Gregory Petrosyan

unread,
Jan 16, 2006, 4:28:53 AM1/16/06
to
_Consistentsy_ is what BDFL rejects, if I understand pep right. As for
me, it's not too god to have similar syntax for really different tasks.
And [1:10] is really not obvious, while [1..10] is.

Steven D'Aprano

unread,
Jan 16, 2006, 5:52:54 AM1/16/06
to
On Mon, 16 Jan 2006 01:01:39 -0800, Gregory Petrosyan wrote:

> Please visit http://www.python.org/peps/pep-0204.html first.
>
> As you can see, PEP 204 was rejected, mostly because of not-so-obvious
> syntax. But IMO the idea behind this pep is very nice. So, maybe
> there's a reason to adopt slightly modified Haskell's syntax? Something
> like
>
> [1,3..10] --> [1,3,5,7,9]

-1 on the introduction of new syntax. Any new syntax.

(I reserve the right to change my mind if somebody comes up with syntax
that I like, but in general, I'm *very* negative on adding to Python's
clean syntax.)

For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange. The only added feature this proposal
introduces is infinite iterators, and they aren't particularly hard to
make:

def arithmetic_sequence(start, step=1):
yield start
while 1:
start += step
yield start

The equivalent generator for a geometric sequence is left as an exercise
for the reader.

If your proposal included support for ranges of characters, I'd be more
interested.

--
Steven.

Paul Rubin

unread,
Jan 16, 2006, 5:58:39 AM1/16/06
to
Steven D'Aprano <st...@REMOVETHIScyber.com.au> writes:
> For finite sequences, your proposal adds nothing new to existing
> solutions like range and xrange.

Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).

> The only added feature this proposal
> introduces is infinite iterators, and they aren't particularly hard to
> make:
>
> def arithmetic_sequence(start, step=1):
> yield start
> while 1:
> start += step
> yield start

Well, that would be itertools.count(start, step) but in general a simple
expression is nicer than 5 lines of code.

> If your proposal included support for ranges of characters, I'd be more
> interested.

There's something to be said for that. Should ['a'..'z'] be a list or
a string?

Xavier Morel

unread,
Jan 16, 2006, 6:51:58 AM1/16/06
to
Paul Rubin wrote:
> There's something to be said for that. Should ['a'..'z'] be a list or
> a string?
To me, the most obvious result would be either a range object as a
result, or always a list/generator of objects (to stay perfectly
consistent). If a range of numbers translate into a list of numbers,
then a range of characters should likewise translate to a list of
characters, and a join would be required to get a regular string. This
also adds more consistency between the two proposals of the initial post
(e.g. list-based range and generator-based range), for while the
list-based range could be expanded into a string a generator-based one
couldn't/shouldn't, and the abstraction breaks (because two constructs
that should be more or less equivalent become extremely different and
can't be swapped transparently).

This would also be consistent with other languages providing a native
"range" object such as Ruby or or Ada ranges.

The only thing that bothers me about the initial proposal is that there
would not, in fact, be any "range object", but merely a syntactic sugar
for list/generator creation. Not that I really mind it, but, well,
syntactic sugar for the purpose of syntactic sugar really doesn't bring
much to the table.

For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods) excludes the end object of the
range definition ('a'..'z') is the range from 'a' to 'z' while
('a'...'z') only ranges from 'a' to 'y').

bearoph...@lycos.com

unread,
Jan 16, 2006, 10:24:43 AM1/16/06
to
Ranges of letters are quite useful, they are used a lot in Delphi/Ada
languages:
"a", "b", "c", "d", "e"...

I like the syntax [1..n], it looks natural enough to me, but I think
the Ruby syntax with ... isn't much natural.
To avoid bugs the following two lines must have the same meaning:
[1..n-1]
[1..(n-1)]

If you don't want to change the Python syntax then maybe the
range/xrange can be extended for chars too:

xrange("a", "z")
range("z", "a", -1)

But for char ranges I think people usually don't want to stop to "y"
(what if you want to go to "z" too? This is much more common than
wanting to stop to "y"), so another possibility is to create a new
function like xrange that generates the last element too:

interval("a", "c") equals to iter("abc")
interval(1, 3) equals to iter([1,2,3])
interval(2, 0, -1) equals to iter([2,1,0])

I have created such interval function, and I use it now and then.

Bye,
bearophile

Steven D'Aprano

unread,
Jan 16, 2006, 11:14:08 AM1/16/06
to
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:

> For those who'd need the (0..n-1) behavior, Ruby features something that
> I find quite elegant (if not perfectly obvious at first), (first..last)
> provides a range from first to last with both boundaries included, but
> (first...last) (notice the 3 periods)

No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some folks
want to undermine that.

*shakes head in amazement*


--
Steven.

Alex Martelli

unread,
Jan 16, 2006, 11:30:45 AM1/16/06
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:
...

> while the traditional
>
> xrange(f(n)-1, -1, -1)
>
> only evaluates it once but is IMO repulsive.

Yep, reversed(range(f(n))) is MUCH better.


Alex

Alex Martelli

unread,
Jan 16, 2006, 11:30:46 AM1/16/06
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

> Steven D'Aprano <st...@REMOVETHIScyber.com.au> writes:
> > For finite sequences, your proposal adds nothing new to existing
> > solutions like range and xrange.
>
> Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).

But not easier than reversed(range(6)) [[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]


Alex

Alex Martelli

unread,
Jan 16, 2006, 11:35:05 AM1/16/06
to

Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).


Alex

Steven D'Aprano

unread,
Jan 16, 2006, 11:52:00 AM1/16/06
to
On Mon, 16 Jan 2006 02:58:39 -0800, Paul Rubin wrote:

> Steven D'Aprano <st...@REMOVETHIScyber.com.au> writes:
>> For finite sequences, your proposal adds nothing new to existing
>> solutions like range and xrange.
>
> Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).

Only in isolation, and arguably not even then. Or do you think that Perl
is much easier to read than Python simply because you can write your
programs in fewer characters?

It looks too much like the list [5,4,0], and is easy to make typos:
[5,4,.0] gives you no syntax error but very different results.

The meaning isn't particular clear: is it supposed to be [start, stop,
step] (the natural expectation for those used to Python slices and ranges)
or [start, next, stop]? It is actually the second, but go back to the
original post by Gregory: after giving examples, he still wrongly
described his proposal as having a "step" parameter. There is no step
parameter -- the step is implied, by subtracting start from next. Such
confusion doesn't bode well.

Python indexing deliberately goes to one-past-the-end counting for a
reason: it helps prevent off-by-one signpost errors. This syntax goes
against that decision, and adds one more thing to memorise about Python:
the end index is not included in the list, except for arithmetic
sequences, where it is, sometimes but not necessarily. In [5,6,10] the end
index 10 is included; in [5,7,10] it isn't.

You've picked the most awkward example of range, I admit. But let's look
at a few others:

[0,..9] versus range(10)
[55, ...73] versus range(55, 74)
[1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, 295] versus range(55, 296, 10)

How often do you find yourself knowing the first two terms of a sequence
but not the step size anyway? Is that a common use case?

>> The only added feature this proposal
>> introduces is infinite iterators, and they aren't particularly hard to
>> make:
>>
>> def arithmetic_sequence(start, step=1):
>> yield start
>> while 1:
>> start += step
>> yield start
>
> Well, that would be itertools.count(start, step) but in general a simple
> expression is nicer than 5 lines of code.

I didn't say that my generator was the only way to produce the required
result, I was pointing out how simple it is. Yes, itertools is the way to
go for this sort of thing.


>> If your proposal included support for ranges of characters, I'd be more
>> interested.
>
> There's something to be said for that. Should ['a'..'z'] be a list or a
> string?

It uses [ something ] syntax, so for consistency with lists and list
comprehensions it should be a list.

But a string would be more practical, since list(['a'..'z']) is easier and
more intuitive than ''.join(['a'..'z']). But I'm not sure that it is
*that* much more practical to deserve breaking the reader's expectation.

So I think the best thing would be to create itertools.chars('a', 'z') or
similar, not new syntax.


--
Steven.

Roy Smith

unread,
Jan 16, 2006, 1:41:26 PM1/16/06
to
Alex Martelli <al...@mail.comcast.net> wrote:
>Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
>sequence, it should be in a syntax that can't FAIL to be noticed, such
>as range(X, endincluded=True).

How about...

for i in (0..x]:
blah

Alex Martelli

unread,
Jan 16, 2006, 3:08:14 PM1/16/06
to
Roy Smith <r...@panix.com> wrote:

The difference between a round parenthesis and a square bracket can
EASILY be overlooked, depending partly on what font you're using.


Alex

Marc 'BlackJack' Rintsch

unread,
Jan 16, 2006, 3:43:12 PM1/16/06
to

That would break most editors "highlight matching brace" functionality.

Ciao,
Marc 'BlackJack' Rintsch

Szabolcs Nagy

unread,
Jan 16, 2006, 4:24:41 PM1/16/06
to
i would love to see a nice, clear syntax instead of
for i in xrange(start, stop, step): ...

because xrange is ugly and iteration over int sequences are important.
we don't need a range() alternative ( [0:10] or [0..10] )
(because no one would ever use range() if there were a nice
integer-for-loop)

there was a proposal (http://www.python.org/peps/pep-0284.html):
for start <= i < stop: ...
but in this way you cannot specify the step parameter and it has some
problems when used in list comprehension.

pep 204 like syntax would be:
for i in (start:stop:step): ...
it is much nicer than xrange, but probably it has some inconsistency
with slicing
(eg (:3)=xrange(3), (-3:)=itertools.count(-3), (:-3)=?, (3::-1)=? )

your .. approach:
for i in (start, start+step .. stop): ...
here start written down twice if it's referred by a name (and if start
is a function call it's evaluated twice)
imho without a step it looks nice:
for i in (start .. stop): ...
but a new syntax would be good only if it can entirely replace the old
one (which then can be made deprecated).

Paul Rubin

unread,
Jan 16, 2006, 4:35:48 PM1/16/06
to
al...@mail.comcast.net (Alex Martelli) writes:
> > Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).
>
> But not easier than reversed(range(6))

Heh, I like that, and reversed(xrange(6)) appears to do the right
thing too. I didn't know about __reversed__ before.

> [[the 5 in one of the two
> expressions in your sentence has to be an offbyone;-)]]

Are you sure? I could easily be missing something, since it's easy
to be offbyone with this stuff, but when I try it I get:

Python 2.4.1 (#1, May 16 2005, 15:19:29)
[GCC 4.0.0 20050512 (Red Hat 4.0.0-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(5,-1,-1)
[5, 4, 3, 2, 1, 0]
>>>

and (skipping the ascii art banner):

Hugs 98: Based on the Haskell 98 standard
Haskell 98 mode: Restart with command line option -98 to enable extensions
Type :? for help
Hugs.Base> [5,4..0]
[5,4,3,2,1,0]
Hugs.Base>

which is equivalent. (Of course, having to use 6 instead of 5 in
the range(...) version invites an offbyone error).

Paul Rubin

unread,
Jan 16, 2006, 5:00:58 PM1/16/06
to
Xavier Morel <xavier...@masklinn.net> writes:
> The only thing that bothers me about the initial proposal is that
> there would not, in fact, be any "range object", but merely a
> syntactic sugar for list/generator creation.

Well, it could create something like an xrange. Maybe that's preferable.

> Not that I really mind it, but, well, syntactic sugar for the
> purpose of syntactic sugar really doesn't bring much to the table.

I don't think this is a valid objection. Python is already full of
syntactic sugar like indentation-based block structure, infix
operators, statements with keyword-dependent syntax, etc. It's that
very sugar that attracts programmers to Python away from comparatively
sugarless languages like Scheme. Indeed, Python is considered by many
to be a sweet language to program in, and they mean that in a nice
way.

If you want, you can think of it as "flavor" rather than "sugar". We
aren't after syntactic minimalism or we'd be using Scheme. The
criterion for adding something like this to Python should be whether
makes the language taste better or not.

Gregory Petrosyan

unread,
Jan 16, 2006, 5:05:22 PM1/16/06
to
Thanks for your replies. So, some problems and possible solutions:

1) [f(n), f(n)-1 .. 0] can be easily catched by interpreter, and f(n)
can be evaluated only once.

2) if you need right border excluded, I think [0 .. n) is very clear
(and consistent with mathematics). And about brakets highlighting... Do
you _really_ need it in this expression? By the way, Python
editors/ide's can be easily extended to support this syntax, and even
to color brakets in [0 .. n) in different colors (so, it'll be very
clear if border is included or not).
But as for me, [0 .. n-1] is better because it looks more like ordinary
list, and we would have no problems on creating generators with
excluded border.

3) Of course, in some cases 'range' is more readable. As for your
examples:

[0,..9] versus range(10)
[55, ...73] versus range(55, 74)
[1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, 295] versus range(55, 296, 10)

a) you use "magic" 10 here...
[0 .. 10) or [0 .. 9] are IMO at least as nice as range(10). And what
about
[1 .. 10] and range(1, 11)? ;-)

b) [55 .. 73] vs range(55, 74)? IMO first is more readable. And you
actually can see what is the last value, while in "range" you think
something like: "if 74 is written here, then last value is 74-1 = 73"

c) [1, 3 .. len(mystr)] vs range(1, len(mystr)+1, 2). As for me, I
should think for 2-3 seconds before I understand what range(1,
len(mystr)+1, 2) stands for :-)
And [1, 3 .. len(mystr)] is _obvious_ for almost everybody.

d) why you wrote '296' in range? Maybe, because you are really
experienced programmer, and you can automatically transform 295 -> 296
(because you _know_ the behaviour of range function). But if your task
is simply to iterate through sequence like n1, n2, n3, ... n(X-1), nX,
then [n1 .. nX] looks more natural, isn't it?

4) Proposed syntax can be easily extended to support chars (or any
other enumeration). (Maybe, without _implied_ :-) step parameter):

['a' .. 'd'] -> ['a','b','c','d'] (let it be a list for consistency)
('x' .. 'z') -> generator that yields 'x', 'y' and 'z' :-)
('a' ..) -> generator that yields english alphabet

Conclusion:
- I don't want to remove 'range' function from use completely. Feel
free to use it ;-)
- The only idea behind my proposal is obviousness. As for me, it was
sometimes slightly difficult to explain to somebody not familiar with
Python what

for n in range(1,10,2):
bla-bla-bla

stands for. Arithmetic sequence IMHO isn't that kind of thing when you
need to know some
special function to use it (is everything OK with my english here?)

Gregory Petrosyan

unread,
Jan 16, 2006, 5:22:56 PM1/16/06
to
Steven D'Aprano wrote:
> Python indexing deliberately goes to one-past-the-end counting for a
> reason: it helps prevent off-by-one signpost errors. This syntax goes
> against that decision, and adds one more thing to memorise about Python:
> the end index is not included in the list, except for arithmetic
> sequences, where it is, sometimes but not necessarily. In [5,6,10] the end
> index 10 is included; in [5,7,10] it isn't.

1) both in [5,6,10] and [5,7,10] 10 is included ;-)
And as for [5,6 .. 10] and [5,7 .. 10]... First one should look
like [5 .. 10] I think. But you are right:
it looks like a problem that in one case 10 is included and in
other not. I should think about it.

2) I think there's nothing to memorise: in mathematics [1,2 .. 10]
includes 10, almost everybody knows
it.
The thing that newcomer should memorise is (IMHO) _not_so_obvious_
behaviour of 'range'. You
may laugh at me, if you want. But look at Cormen's pseudocode: "for
j=1 to n" includes 'n'. (And
indexing starts from 1, not from 0, but that's another story).
Also, for non-programmer including the
borders is obvious, not otherwise.

Paul Rubin

unread,
Jan 16, 2006, 5:32:03 PM1/16/06
to
"Gregory Petrosyan" <gregory....@gmail.com> writes:
> 1) both in [5,6,10] and [5,7,10] 10 is included ;-)
> And as for [5,6 .. 10] and [5,7 .. 10]... First one should look
> like [5 .. 10] I think. But you are right:
> it looks like a problem that in one case 10 is included and in
> other not. I should think about it.

[5,7 .. 10] means [5,7,9,11,13,15, ... ] limited to x<=10, so that
means [5,7,9]. 10 is not included.

Paul Rubin

unread,
Jan 16, 2006, 5:45:43 PM1/16/06
to
"Gregory Petrosyan" <gregory....@gmail.com> writes:
> 1) [f(n), f(n)-1 .. 0] can be easily catched by interpreter, and f(n)
> can be evaluated only once.

I think it would be counterintuitive for the interpreter to do that.
If I type f(n) twice I expect it to be evaluated twice.

> 2) if you need right border excluded, I think [0 .. n) is very clear
> (and consistent with mathematics).

Oh man, that's really ugly. I'm not crazy about Ruby's "..." either
though I guess it's ok. Using colon for non-inclusion might be more
Python-like since it resembles both the syntax and behavior of an
existing python range:

[0.. : n]

(hmm, it does look kind of ugly).

> 3) Of course, in some cases 'range' is more readable. As for your
> examples:
>
> [0,..9] versus range(10)
> [55, ...73] versus range(55, 74)
> [1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
> [55, 65, 295] versus range(55, 296, 10)

Those examples should be written:

[0 .. 9] versus range(10)
[55 .. 73] versus range(55,74)
[1, 3, .. len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, .. 295] versus range(55, 296, 10)

I find the ".." version more readable than the "range" version for
all four cases. YMMV.

> 4) Proposed syntax can be easily extended to support chars (or any
> other enumeration). (Maybe, without _implied_ :-) step parameter):
>
> ['a' .. 'd'] -> ['a','b','c','d'] (let it be a list for consistency)

Hmm:

Hugs.Base> ['a'..'d']
"abcd"
Hugs.Base>

Note that "abcd" in Haskell is actually a list of chars.

> ('a' ..) -> generator that yields english alphabet

I think this has to be an infinite generator or one that yields all
the ascii chars starting with 'a'.

Xavier Morel

unread,
Jan 16, 2006, 11:43:45 AM1/16/06
to
Steven, I never said that Python should use this syntax, I merely showed
how it was done in Ruby.

It's nothing more than a ... basis of discussion... not a "I want that
!!ONE" post (if I did, i'd be using Ruby and posting on c.l.r)

(and you didn't what by the way?)

Xavier Morel

unread,
Jan 16, 2006, 11:44:21 AM1/16/06
to
Steven, I never said that Python should use this syntax, I merely showed
how it was done in Ruby.

It's nothing more than a ... basis of discussion... not a "I want that
!!ONE" post (if I did, i'd be using Ruby and posting on c.l.r)

(and you didn't what by the way?)

Ok scratch that, you didn't notice the 3 periods.

Tom Anderson

unread,
Jan 16, 2006, 7:53:55 PM1/16/06
to
On Mon, 16 Jan 2006, it was written:

> There's something to be said for that. Should ['a'..'z'] be a list or a
> string?

And while we're there, what should ['aa'..'zyzzogeton'] be?

tom

--
Socialism - straight in the mainline!

Tom Anderson

unread,
Jan 16, 2006, 8:20:14 PM1/16/06
to
On Mon, 16 Jan 2006, Gregory Petrosyan wrote:

> Please visit http://www.python.org/peps/pep-0204.html first.
>
> As you can see, PEP 204 was rejected, mostly because of not-so-obvious
> syntax. But IMO the idea behind this pep is very nice.

Agreed. Although i have to say, i like the syntax there - it seems like a
really natural extension of existing syntax.

> So, maybe there's a reason to adopt slightly modified Haskell's syntax?

Well, i do like the .. - 1..3 seems like a natural way to write a range.
I'd find 1...3 more natural, since an ellipsis has three dots, but it is
slightly more tedious.

The natural way to implement this would be to make .. a normal operator,
rather than magic, and add a __range__ special method to handle it. "a ..
b" would translate to "a.__range__(b)". I note that Roman Suzi proposed
this back in 2001, after PEP 204 was rejected. It's a pretty obvious
implementation, after all.

> Something like
>
> [1,3..10] --> [1,3,5,7,9]

> (1,3..10) --> same values as above, but return generator instead of
> list
> [1..10] --> [1,2,3,4,5,6,7,8,9,10]
> (1 ..) --> 'infinite' generator that yield 1,2,3 and so on
> (-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on

-1. Personally, i find the approach of specifying the first two elements
*absolutely* *revolting*, and it would consistently be more awkward to use
than a start/step/stop style syntax. Come on, when do you know the first
two terms but not the step size?

> 1) "[]" means list, "()" means generator

Yuck. Yes, i know it's consistent with list comps and genexps, but yuck to
those too!

Instead, i'd like to see lazy lists used here - these look like lists, and
can be used exactly like a list, but if all you want to do is iterate over
them, they don't need to instantiate themselves in memory, so they're as
efficient as an iterator. The best of both worlds! I've written a sketch
of a generic lazy list:

http://urchin.earth.li/~twic/lazy.py

Note that this is what xrange does already (as i've just discovered).

Tom Anderson

unread,
Jan 16, 2006, 8:21:21 PM1/16/06
to

How about first,,last? Harder to do by mistake, but pretty horrible in its
own way.

Paul Rubin

unread,
Jan 16, 2006, 8:40:43 PM1/16/06
to
Tom Anderson <tw...@urchin.earth.li> writes:
> The natural way to implement this would be to make .. a normal
> operator, rather than magic, and add a __range__ special method to
> handle it. "a .. b" would translate to "a.__range__(b)". I note that
> Roman Suzi proposed this back in 2001, after PEP 204 was
> rejected. It's a pretty obvious implementation, after all.

Interesting, but what do you do about the "unary postfix" (1 ..)
infinite generator?

> > (-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on
>
> -1. Personally, i find the approach of specifying the first two
> elements *absolutely* *revolting*, and it would consistently be more
> awkward to use than a start/step/stop style syntax. Come on, when do
> you know the first two terms but not the step size?

Usually you know both, but showing the first two elements makes
sequence more visible. I certainly like (1,3..9) better than (1,9;2)
or whatever.

> > 1) "[]" means list, "()" means generator
> Yuck. Yes, i know it's consistent with list comps and genexps, but
> yuck to those too!

I'd be ok with getting rid of [] and just having generators or
xrange-like class instances. If you want to coerce one of those to a
list, you'd say list((1..5)) instead of [1..5].

Message has been deleted

Paul Rubin

unread,
Jan 17, 2006, 1:30:08 AM1/17/06
to
Dennis Lee Bieber <wlf...@ix.netcom.com> writes:
> What would be expected from
> [1, 3, 6 .. 20]
> ???

In Haskell:

Hugs.Base> [1,3,6..20]
ERROR - Syntax error in expression (unexpected `..')
Hugs.Base>

> Again, I see [1, 3, 6, 7, 8, 9, 10, ... , 18, 19]

You'd write that in Haskell as 1:3:[6..19]. In Python I guess
you'd say [1,3]+[6..19].

Gregory Petrosyan

unread,
Jan 17, 2006, 3:45:04 AM1/17/06
to
Some ideas:

1) Let [a,b .. c] be *ordinary list* !
Just like [1,2,3]. Are there any questions why 3 is included in
[1,2,3]? IMO it's more correct to think about [first, next .. last] as
about syntax for list creation, but not as about
"syntax-to-replace-range-function". (And, because it's an ordinary
list, you could iterate through it in usual way: "for each element of
list do...")

2) [5 .. 0] -> [5,4,3,2,1,0]
So, if "next" is omited, let the default step be 1 if "first" < "last"
and -1 otherwise.

Antoon Pardon

unread,
Jan 17, 2006, 4:11:43 AM1/17/06
to
Op 2006-01-16, Alex Martelli schreef <al...@mail.comcast.net>:

Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.

3) Allow slice notation to be used anywhere a value can be
used.

4) Provide a number of extra operators on slices.
__neg__ (reverses the slice)
__and__ gives the intersection of two slices
__or__ gives the union of two slices

5) Provide sequences with a range (or slice) method.
This would provide an iterator that iterates over
the indexes of the sequences. A slice could be
provided


for i in xrange(6):

would then become

for i in (0:6):

for a reversed sequence

for i in reversed(xrange(6)):

would become

for i in - (0:6):


for i, el in enumerate(sequence):

would become

for i in sequence.range():
el = sequence[i]

But the advantage is that this would still work when
someone subclasses a list so that it start index
is an other number but 0.

If you only wanted every other index one could do
the following

for i in sequence.range(::2):

which would be equivallent to

for i in sequence.range() & (::2):

--
Antoon Pardon

Paul Rubin

unread,
Jan 17, 2006, 4:49:10 AM1/17/06
to
"Gregory Petrosyan" <gregory....@gmail.com> writes:
> 2) [5 .. 0] -> [5,4,3,2,1,0]
> So, if "next" is omited, let the default step be 1 if "first" < "last"
> and -1 otherwise.

So what do you want [a..b] to do? Dynamically decide what direction
to go? Ugh!

Gregory Petrosyan

unread,
Jan 17, 2006, 5:00:01 AM1/17/06
to
Hmm, and why not? Or you realy hate such behaviour?

Note, everything I post here is just some ideas I want to discuss, and
to make these ideas better after discussion. And this particular idea
needs to be discussed, too.

Steven Bethard

unread,
Jan 17, 2006, 12:51:24 PM1/17/06
to
Antoon Pardon wrote:
> Why don't we give slices more functionality and use them.
> These are a number of ideas I had. (These are python3k ideas)
>
> 1) Make slices iterables. (No more need for (x)range)
>
> 2) Use a bottom and stop variable as default for the start and
> stop attribute. top would be a value that is greater than
> any other value, bottom would be a value smaller than any
> other value.

Just checking your intent here. What should these do?

(2:5) # should give me 2, 3, 4
(2:5:-1) # infinite iterator 2, 1, 0, ...?
(:5) # start at -infinity? what does that even mean?
(:5:-1) # start at -infinity and go backwards?!!

I suspect you should be raising some sort of exception if the start
isn't defined.

STeVe

Gregory Petrosyan

unread,
Jan 17, 2006, 5:39:13 PM1/17/06
to
Hey guys, this proposal has already been rejected (it is the PEP 204).

Tom Anderson

unread,
Jan 17, 2006, 6:57:03 PM1/17/06
to
On Tue, 16 Jan 2006, it was written:

> Tom Anderson <tw...@urchin.earth.li> writes:
>
>> The natural way to implement this would be to make .. a normal
>> operator, rather than magic, and add a __range__ special method to
>> handle it. "a .. b" would translate to "a.__range__(b)". I note that
>> Roman Suzi proposed this back in 2001, after PEP 204 was rejected. It's
>> a pretty obvious implementation, after all.
>
> Interesting, but what do you do about the "unary postfix" (1 ..)
> infinite generator?

1.__range__(None)

>>> (-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on
>>
>> -1. Personally, i find the approach of specifying the first two
>> elements *absolutely* *revolting*, and it would consistently be more
>> awkward to use than a start/step/stop style syntax. Come on, when do
>> you know the first two terms but not the step size?
>
> Usually you know both, but showing the first two elements makes sequence
> more visible. I certainly like (1,3..9) better than (1,9;2) or
> whatever.

I have to confess that i don't have a pretty three-argument syntax to
offer as an alternative to yours. But i'm afraid i still don't like yours.
:)

>>> 1) "[]" means list, "()" means generator
>> Yuck. Yes, i know it's consistent with list comps and genexps, but yuck
>> to those too!
>
> I'd be ok with getting rid of [] and just having generators or
> xrange-like class instances. If you want to coerce one of those to a
> list, you'd say list((1..5)) instead of [1..5].

Sounds good. More generally, i'd be more than happy to get rid of list
comprehensions, letting people use list(genexp) instead. That would
obviously be a Py3k thing, though.

tom

--
Taking care of business

Tom Anderson

unread,
Jan 17, 2006, 7:06:57 PM1/17/06
to

+5

> for i, el in enumerate(sequence):
>
> would become
>
> for i in sequence.range():
> el = sequence[i]

That one, i'm not so happy with - i quite like enumerate; it communicates
intention very clearly. I believe enumerate is implemented with iterators,
meaning it's potentially more efficient than your approach, too. And since
enumerate works on iterators, which yours doesn't, you have to keep it
anyway. Still, both would be possible, and it's a matter of taste.

> But the advantage is that this would still work when someone subclasses
> a list so that it start index is an other number but 0.

It would be possible to patch enumerate to do the right thing in those
situations - it could look for a range method on the enumerand, and if it
found one, use it to generate the indices. Like this:

def enumerate(thing):
if (hasattr(thing, "range")):
indices = thing.range()
else:
indices = itertools.count()
return itertools.izip(indices, thing)

> If you only wanted every other index one could do the following
>
> for i in sequence.range(::2):
>
> which would be equivallent to
>
> for i in sequence.range() & (::2):

Oh, that is nice. Still, you could also extend enumerate to take a range
as an optional second parameter and do this with it. Six of one, half a
dozen of the other, i suppose.

Xavier Morel

unread,
Jan 17, 2006, 7:10:49 AM1/17/06
to
Paul Rubin wrote:
> I don't think this is a valid objection. Python is already full of
> syntactic sugar like indentation-based block structure, infix
> operators, statements with keyword-dependent syntax, etc. It's that
> very sugar that attracts programmers to Python away from comparatively
> sugarless languages like Scheme. Indeed, Python is considered by many
> to be a sweet language to program in, and they mean that in a nice
> way.
>
> If you want, you can think of it as "flavor" rather than "sugar". We
> aren't after syntactic minimalism or we'd be using Scheme. The
> criterion for adding something like this to Python should be whether
> makes the language taste better or not.

I don't know, most of the syntactic sugar I see in Python brings
something to the language, or trivialize the generation of structures
and constructs that may be complex or awkward without it, it has a
natural, honey-ish sweetness full of flavor, it does not taste like some
cancer-spawning artificial sweetener ;-)

Steven Bethard

unread,
Jan 17, 2006, 7:57:31 PM1/17/06
to
Gregory Petrosyan wrote:
> Hey guys, this proposal has already been rejected (it is the PEP 204).

No, this is a subtly different proposal. Antoon is proposing *slice*
literals, not *range* literals. Note that "confusion between ranges and
slice syntax" was one of the reasons for rejection of `PEP 204`_. Which
means that if Antoon really wants his proposal to go through, he
probably needs to make sure that slice literals are clearly distinct
from range literals.

.. _PEP 204: http://www.python.org/peps/pep-0204.html

STeVe

Alex Martelli

unread,
Jan 18, 2006, 1:39:32 AM1/18/06
to
Tom Anderson <tw...@urchin.earth.li> wrote:
...

> Sounds good. More generally, i'd be more than happy to get rid of list
> comprehensions, letting people use list(genexp) instead. That would
> obviously be a Py3k thing, though.

I fully agree, but the BDFL has already (tentatively, I hope) Pronounced
that the [...] form will stay in Py3K as syntax sugar for list(...). I
find that to be a truly hateful prospect, but that's the prospect:-(.


Alex

Antoon Pardon

unread,
Jan 18, 2006, 2:57:57 AM1/18/06
to
Op 2006-01-17, Steven Bethard schreef <steven....@gmail.com>:

> Antoon Pardon wrote:
>> Why don't we give slices more functionality and use them.
>> These are a number of ideas I had. (These are python3k ideas)
>>
>> 1) Make slices iterables. (No more need for (x)range)
>>
>> 2) Use a bottom and stop variable as default for the start and
>> stop attribute. top would be a value that is greater than
>> any other value, bottom would be a value smaller than any
>> other value.
>
> Just checking your intent here. What should these do?
>
> (2:5) # should give me 2, 3, 4

Yes.

> (2:5:-1) # infinite iterator 2, 1, 0, ...?

No it would give nothing, think xrange(2,5,-1)


> (:5) # start at -infinity? what does that even mean?

I'm still thinking abouth this. My preffered idea now it
it would be an endless loop returning bottom elements.

> (:5:-1) # start at -infinity and go backwards?!!

This would give nothing. The idea is that if the step is negative
the iteration stops when the (hidden) index is equal or smaller
than the stop value. This is true here from the start.

> I suspect you should be raising some sort of exception if the start
> isn't defined.

That is a possibility too.

--
Antoon Pardon

Antoon Pardon

unread,
Jan 18, 2006, 3:10:19 AM1/18/06
to
Op 2006-01-17, Gregory Petrosyan schreef <gregory....@gmail.com>:

> Hey guys, this proposal has already been rejected (it is the PEP 204).
>

No it isn't. In my proposal [1, 2:8, 8] would be equivallent to
[1, slice(2,8), 8]. If someone would want the list [1,2,3,4,5,6,7,8]
with this notation, I would propose a flatten function witch would
work with iterators too, so that flat([1, 2:8, 8]) would be the
same as flat([1, range(2,8), 8])

In my view python already has slice literals. You are only limited
in using this literals an index in subscription. I think this is
a rathter pointless limitation and that literal slices could be
very usefull when made into literals and usable as parameters.

--
Antoon Pardon

Antoon Pardon

unread,
Jan 18, 2006, 3:35:12 AM1/18/06
to
Op 2006-01-18, Tom Anderson schreef <tw...@urchin.earth.li>:

Fine by me. I'm not against enumerate.

>> If you only wanted every other index one could do the following
>>
>> for i in sequence.range(::2):
>>
>> which would be equivallent to
>>
>> for i in sequence.range() & (::2):
>
> Oh, that is nice. Still, you could also extend enumerate to take a range
> as an optional second parameter and do this with it. Six of one, half a
> dozen of the other, i suppose.

Yes you could probably do so and I'm not against it, I just think that
range would be a better base on which you can build enumerate and other
things than that enumenrate can be a base. e.g. __len__ could be eliminated
and len would be defined as:

def len(seq):
rng = seq.range()
return rng.stop - rng.start

Antoon Pardon

unread,
Jan 18, 2006, 4:12:31 AM1/18/06
to
Op 2006-01-16, Gregory Petrosyan schreef <gregory....@gmail.com>:

> Please visit http://www.python.org/peps/pep-0204.html first.
>
> As you can see, PEP 204 was rejected, mostly because of not-so-obvious
> syntax. But IMO the idea behind this pep is very nice. So, maybe
> there's a reason to adopt slightly modified Haskell's syntax? Something

> like
>
> [1,3..10] --> [1,3,5,7,9]
> (1,3..10) --> same values as above, but return generator instead of
> list
> [1..10] --> [1,2,3,4,5,6,7,8,9,10]
> (1 ..) --> 'infinite' generator that yield 1,2,3 and so on

> (-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on
>
> So,

> 1) "[]" means list, "()" means generator
> 2) the "start" is required, "step" and "end" are optional.
>
> Also, this can be nicely integrated with enumerations (if they will
> appear in python). Haskell is also example of such integration.

With some abuse of the language one can already do a number of things
in python2.4 now.

import sys
from types import SliceType

class vslice(object):

def __init__(self, fun):
self.fun = fun

def __getitem__(self, inx):
if not isinstance(inx, tuple):
inx = inx,
return self.fun(*inx)

@vslice
def rnglst(*arg):
lst = []
for el in arg:
if type(el) is SliceType:
start = el.start or 0
stop = el.stop or sys.maxint
step = el.step or 1
if step > 0:
while start < stop:
lst.append(start)
start += step
else:
while start > stop:
lst.append(start)
start += step
else:
lst.append(el)
return lst

rnglst[3,4,5] --> [3, 4, 5]
rnglst[1, 2:8] --> [1, 2, 3, 4, 5, 6, 7]
rnglst[3:9:2, 21:6:-3] --> [3, 5, 7, 21, 18, 15, 12, 9]

--
Antoon Pardon

Steven Bethard

unread,
Jan 18, 2006, 12:01:20 PM1/18/06
to
Alex Martelli wrote:

> Tom Anderson <tw...@urchin.earth.li> wrote:
>> Sounds good. More generally, i'd be more than happy to get rid of list
>> comprehensions, letting people use list(genexp) instead. That would
>> obviously be a Py3k thing, though.
>
> I fully agree, but the BDFL has already (tentatively, I hope) Pronounced
> that the [...] form will stay in Py3K as syntax sugar for list(...).

Do you have a link for that? I hadn't seen the pronouncement, and
http://wiki.python.org/moin/Python3%2e0 still lists that as an open issue.

> I find that to be a truly hateful prospect

I'm not sure I find it truly hateful, but definitely unnecessary.
TOOWTDI and all...

STeVe

Paul Rubin

unread,
Jan 18, 2006, 12:20:44 PM1/18/06
to
Steven Bethard <steven....@gmail.com> writes:
> > I fully agree, but the BDFL has already (tentatively, I hope)
> > Pronounced
> > that the [...] form will stay in Py3K as syntax sugar for list(...).
> > I find that to be a truly hateful prospect
>
> I'm not sure I find it truly hateful, but definitely
> unnecessary. TOOWTDI and all...

Well, [...] notation for regular lists (as opposed to list
comprehensions) is also unnecessary since we could use "list((a,b,c))".

Are we -really- after syntax minimalism? And if we are, shouldn't we
just all switch to using Scheme?

Alex Martelli

unread,
Jan 18, 2006, 9:31:34 PM1/18/06
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:
...
> Well, [...] notation for regular lists (as opposed to list
> comprehensions) is also unnecessary since we could use "list((a,b,c))".

Indeed, there's no reason list couldn't accept multiple arguments as an
alternative to one sequence argument, just like min and max. This would
make list(a, b, c) work just perfectly.

> Are we -really- after syntax minimalism? And if we are, shouldn't we
> just all switch to using Scheme?

No, we're not after syntax minimalism (which might justify moving to
Lisp or Scheme) but syntax that's simple and readable. I see no
credibly simple and readable alternative to {a:b, c:d} dictionary
display syntax, for example; dict(((a,b),(c,d))) just wouldn't cut it,
because of the "parentheses overload" (double entendre intended). But
list has no such problem, and there's really no added value of clarity
and readability in [a,b,c] vs list(a,b,c). And don't tell me that the
added value is that brackets "suggest lists", since they're used to
index tuples and dicts just as well;-). So the only reason they may
"suggest lists" is that you're used to that display form in Python, but
there's no real reason to HAVE a display form for lists at all, IMHO.


Alex

André

unread,
Jan 18, 2006, 11:37:35 PM1/18/06
to
Alex Martelli wrote:
> Paul Rubin <http://phr...@NOSPAM.invalid> wrote:
> ...
> > Well, [...] notation for regular lists (as opposed to list
> > comprehensions) is also unnecessary since we could use "list((a,b,c))".
>
[snip] ... or should that be list(snip)?

> But
> list has no such problem, and there's really no added value of clarity
> and readability in [a,b,c] vs list(a,b,c). And don't tell me that the
> added value is that brackets "suggest lists", since they're used to
> index tuples and dicts just as well;-). So the only reason they may
> "suggest lists" is that you're used to that display form in Python, but
> there's no real reason to HAVE a display form for lists at all, IMHO.
>
> Alex

I'm reading this and thinking "I disagree (?!) with Alex Martelli;
[a,b,c] is so much easier to read and understand than list(a,b,c)"...

And then it dawned on me that, the first time I learned about list
comprehension (a little over a year ago, in A.M.'s Python in a
Nutshell), I had all kinds of trouble getting used to seeing right away
that the square brackets were an indication that a list was created.
At the time, I would have totally agreed that list(a,b,c) is *much*
clearer...
{or expressions like a = list(x for x in range(1)) ...}

*Now*, I prefer [], as I have been "trained" that way... but, thinking
back, and thinking about teaching Python, I'd have to agree with Alex's
point (not that anyone would care about *my* opinion ;-)

André

Steven D'Aprano

unread,
Jan 18, 2006, 11:48:40 PM1/18/06
to
Steven Bethard wrote:

> I'm not sure I find it truly hateful, but definitely unnecessary.
> TOOWTDI and all...

TOOWTDI Considered Harmful.

There is confusion between "There's Only One Way To Do
It" and "There's One Obvious Way To Do It". The first
is pejorative, harmful if it were true but stupid since
it is never true. There is never only one way to do
anything but the most trivial things. Perhaps there is
only one way to do a null-op, (pass), but that's about it.

Even when people mean One Obvious and not Only One, it
is still harmful because the emphasis is wrong. The
emphasis is on the *restrictive nature* of a language
which merely gives one obvious way of doing things.

The motto from the Zen of Python is far more nuanced
and sensible, although not as snappy:

"There should be one-- and preferably only one
--obvious way to do it."

The emphasis in the Zen is about *enabling* Python to
have at least one (and preferably only one) obvious
ways to do things. The Zen is about making sure that
solutions are obvious in Python; TOOWTDI is about the
paucity of solutions in the language, and I cringe
every time I see it. See the difference?


--
Steven.

Paul Rubin

unread,
Jan 19, 2006, 12:00:09 AM1/19/06
to
al...@mail.comcast.net (Alex Martelli) writes:
> > Well, [...] notation for regular lists (as opposed to list
> > comprehensions) is also unnecessary since we could use "list((a,b,c))".
>
> Indeed, there's no reason list couldn't accept multiple arguments as an
> alternative to one sequence argument, just like min and max. This would
> make list(a, b, c) work just perfectly.

What should the output of "print list(1,2,3)" be? Is there a really
good reason to make it different from the input syntax?

What should list(list(1,2,3)) be? Should "list" really work
completely differently depending on whether it has a single arg or
multiple args?

How would you make a one-element list, which we'd currently write as [3]?
Would you have to say list((3,))?

> I see no credibly simple and readable alternative to {a:b, c:d}
> dictionary display syntax, for example; dict(((a,b),(c,d))) just
> wouldn't cut it, because of the "parentheses overload" (double
> entendre intended).

dict(a=b,c=d)

I don't really see why keyword arg names have to be identifiers
either. foo(3=4) can pass a **kwargs containing {3:4}.

> But list has no such problem, and there's really no added value of
> clarity and readability in [a,b,c] vs list(a,b,c).

That's subjective at best. Even Scheme doesn't make you use
extra keywords to write something as fundamental as a list. ;-)

> So the only reason they may "suggest lists" is that you're used to
> that display form in Python, but there's no real reason to HAVE a
> display form for lists at all, IMHO.

Well, what's supposed to happen when you print a list then?

Alex Martelli

unread,
Jan 19, 2006, 1:55:19 AM1/19/06
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:
...
> What should the output of "print list(1,2,3)" be? Is there a really
> good reason to make it different from the input syntax?

If we assume that str and repr must keep coinciding for lists (and why
not), no reason -- just like, say, today:

>>> print set([1,2,3])
set([1, 2, 3])

input and output could be identical. Do YOU have any good reason why
sets should print out as set(...) and lists should NOT print out as
list(...)? Is 'list' somehow "deeper" than 'set', to deserve a special
display-form syntax which 'set' doesn't get? Or are you enshrining a
historical accident to the level of an erroneously assumed principle?

> What should list(list(1,2,3)) be? Should "list" really work
> completely differently depending on whether it has a single arg or
> multiple args?

It works just fine for max and min, why should list be different?

> How would you make a one-element list, which we'd currently write as [3]?
> Would you have to say list((3,))?

Yep. I don't particularly like the "mandatory trailing comma" in the
tuple's display form, mind you, but, if it's good enough for tuples, and
good enough for sets (how else would you make a one-element set?), it's
good enough for lists too. If we can find a nicer singleton-form, let's
apply it uniformly to all genexps, and list(<niceform>) will be fine!-)


> > I see no credibly simple and readable alternative to {a:b, c:d}
> > dictionary display syntax, for example; dict(((a,b),(c,d))) just
> > wouldn't cut it, because of the "parentheses overload" (double
> > entendre intended).
>
> dict(a=b,c=d)
>
> I don't really see why keyword arg names have to be identifiers
> either. foo(3=4) can pass a **kwargs containing {3:4}.

I much prefer the current arrangement where dict(a=b,c=d) means {'a':b,
'c':d} -- it's much more congruent to how named arguments work for every
other case. Would you force us to quote argument names in EVERY
functioncall...?!


> > But list has no such problem, and there's really no added value of
> > clarity and readability in [a,b,c] vs list(a,b,c).
>
> That's subjective at best. Even Scheme doesn't make you use
> extra keywords to write something as fundamental as a list. ;-)

In Python, a list is no more fundamental than a dict or a set (in maths,
I guess a set IS more fundamental, of course, but Python's not
necessarily congruent to maths). Only tuples deserve special treatment
on semantical grounds (as they're used to pass multiple arguments to
functions or return multiple values for function). Of course "it's
subjective" -- obviously, some people prize conciseness above everything
else (otherwise APL and its successors would have been stillborn),
others prize verbosity (or else Cobol would never have been coinceived);
but Python's "Middle Way" tends to strike a good balance. IMHO, the
balance would be better served by spelling list(1,2,3) rather than
[1,2,3] (but the BDFL disagrees, so my opinion doesn't matter much).


> > So the only reason they may "suggest lists" is that you're used to
> > that display form in Python, but there's no real reason to HAVE a
> > display form for lists at all, IMHO.
>
> Well, what's supposed to happen when you print a list then?

Just the same as when you print a set: <typename>(<args>).


Alex

Antoon Pardon

unread,
Jan 19, 2006, 2:50:26 AM1/19/06
to
Op 2006-01-19, Alex Martelli schreef <al...@mail.comcast.net>:

>> What should list(list(1,2,3)) be? Should "list" really work
>> completely differently depending on whether it has a single arg or
>> multiple args?
>
> It works just fine for max and min, why should list be different?

Well IMO it works fine for max and min because people rarely want
the minimum or maximum over a sequence of tuples or lists.

But how would you make the following list: [(1,3)]

As far as I understand that would have to become:

list(((1,3),))

And [[3]] would have to become:

list((list((3,)),))


In my opinion the bracket notation wins the clarity contest
handsdown in this case and IMO these cases occure frequently
enough.

--
Antoon Pardon

Paul Rubin

unread,
Jan 19, 2006, 3:14:35 AM1/19/06
to
Antoon Pardon <apa...@forel.vub.ac.be> writes:
> And [[3]] would have to become:
> list((list((3,)),))
> In my opinion the bracket notation wins the clarity contest
> handsdown in this case and IMO these cases occure frequently
> enough.

I'm convinced now. Until that, I could have gone either way.

Steven D'Aprano

unread,
Jan 19, 2006, 3:53:36 AM1/19/06
to
Alex Martelli wrote:

> I much prefer the current arrangement where dict(a=b,c=d) means {'a':b,
> 'c':d} -- it's much more congruent to how named arguments work for every
> other case. Would you force us to quote argument names in EVERY
> functioncall...?!


Hmmm... should these two forms give different results?

>>> a = 0
>>> b = 1
>>> A = {a: b}
>>> B = dict(a=b)
>>> A, B
({0: 1}, {'a': 1})

I feel very uncomfortable about that. Am I alone?


--
Steven.

Fredrik Lundh

unread,
Jan 19, 2006, 4:27:11 AM1/19/06
to pytho...@python.org
Steven D'Aprano wrote:

yes.

</F>

Kent Johnson

unread,
Jan 19, 2006, 7:24:08 AM1/19/06
to
Paul Rubin wrote:

> al...@mail.comcast.net (Alex Martelli) writes:
>>I see no credibly simple and readable alternative to {a:b, c:d}
>>dictionary display syntax, for example; dict(((a,b),(c,d))) just
>>wouldn't cut it, because of the "parentheses overload" (double
>>entendre intended).
>
>
> dict(a=b,c=d)
>
> I don't really see why keyword arg names have to be identifiers
> either. foo(3=4) can pass a **kwargs containing {3:4}.

The keywords need to be parseable; what would be the keyword form of
{ '3=4, 5=' : 6 }
or
{ '3=4); print "gotcha"; dict(6=' : 7 }
?

Kent

Hans Nowak

unread,
Jan 19, 2006, 11:59:02 AM1/19/06
to
Alex Martelli wrote:

> Do YOU have any good reason why
> sets should print out as set(...) and lists should NOT print out as
> list(...)? Is 'list' somehow "deeper" than 'set', to deserve a special
> display-form syntax which 'set' doesn't get? Or are you enshrining a
> historical accident to the level of an erroneously assumed principle?

(I haven't been following this thread much, so I can't tell if you're
actually arguing for this change, or that you are just playing devil's
advocate...)

I would have liked to say that lists are a fundamental data type, much
more so than a set... but in reality that seems to be a matter of taste
and priorities. Pascal, for example, has a set literal, but no list
literal; in fact, it doesn't even have a built-in list type.

--
Hans Nowak
http://zephyrfalcon.org/

Steven Bethard

unread,
Jan 19, 2006, 1:17:56 PM1/19/06
to
Steven D'Aprano wrote:
> Steven Bethard wrote:
>
>> I'm not sure I find it truly hateful, but definitely unnecessary.
>> TOOWTDI and all...
>
[snip]

>
> Even when people mean One Obvious and not Only One, it is still harmful
> because the emphasis is wrong. The emphasis is on the *restrictive
> nature* of a language which merely gives one obvious way of doing things.

Perhaps just the *perceived* emphasis is wrong? Let me elaborate...

My intent was only to indicate that I don't think we *gain* anything by
having two forms that are quite similar both syntactically and
semantically. Both [...] and list(...) use exactly the same syntax for
the inner expression[1]. Both use parentheses/brackets, so the inner
expression can be broken across multiple lines in the same ways. Both
produce the same result, a list created in the appropriate manner.

So why have both? That's all I meant by TOOWTDI.

FWIW, I've posted my arguments for removing the redundant [...]
expression in Python 3.0 at:

http://wiki.python.org/moin/Python3%2e0Suggestions#preview

STeVe

[1] Reminder: I'm *not* arguing for list literals to look like
list(...), only for list comprehensions to look that way.

Roberto De Almeida

unread,
Jan 19, 2006, 1:31:11 PM1/19/06
to
How about this hack:

>>> import types
>>> class lazy_range(object):
... def __getitem__(self, l):
... start = l[0]
...
... if isinstance(l[1], types.EllipsisType):
... step = 1
... if len(l) > 2:
... stop = l[2]
... else:
... stop = None
... else:
... step = l[1] - l[0]
... if len(l) > 3:
... stop = l[3]
... else:
... stop = None
...
... for i in xrange(start, stop+1, step):
... yield i
>>>
>>> l = lazy_range()
>>> print [i for i in l[1,3,...,10]]
[1, 3, 5, 7, 9]
>>> print [i for i in l[1,...,10]]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Paul Rubin

unread,
Jan 19, 2006, 7:49:14 PM1/19/06
to
al...@mail.comcast.net (Alex Martelli) writes:
> I much prefer the current arrangement where dict(a=b,c=d) means {'a':b,
> 'c':d} -- it's much more congruent to how named arguments work for every
> other case. Would you force us to quote argument names in EVERY
> functioncall...?!

Ehh, ok. There could be some special marker to evaluate the lhs, but
the present method is fine too.

Paul Rubin

unread,
Jan 20, 2006, 5:58:42 PM1/20/06
to
al...@mail.comcast.net (Alex Martelli) writes:
> > How would you make a one-element list, which we'd currently write as [3]?
> > Would you have to say list((3,))?
>
> Yep. I don't particularly like the "mandatory trailing comma" in the
> tuple's display form, mind you, but, if it's good enough for tuples, and
> good enough for sets (how else would you make a one-element set?),

If you really want to get rid of container literals, maybe the best
way is with constructor functions whose interfaces are slightly
different from the existing type-coercion functions:

listx(1,2,3) => [1, 2, 3]
listx(3) => [3]
listx(listx(3)) => [[3]]
dictx((a,b), (c,d)) => {a:b, c:d}
setx(a,b,c) => Set((a,b,c))

listx/dictx/setx would be the display forms as well as the constructor forms.

Tom Anderson

unread,
Jan 21, 2006, 10:33:41 AM1/21/06
to
On Fri, 20 Jan 2006, it was written:

Could these even replace the current forms? If you want the equivalent of
list(sometuple), write list(*sometuple). With a bit of cleverness down in
the worky bits, this could be implemented to avoid the apparent overhead
of unpacking and then repacking the tuple. In fact, in general, it would
be nice if code like:

def f(*args):
fondle(args)

foo = (1, 2, 3)
f(*foo)

Would avoid the unpack/repack.

The problem is that you then can't easily do something like:

mytable = ((1, 2, 3), ("a", "b", "c"), (Tone.do, Tone.re, Tone.mi))
mysecondtable = map(list, mytable)

Although that's moderately easy to work around with possibly the most
abstract higher-order-function i've ever written:

def star(f):
def starred_f(args):
return f(*args)
return starred_f

Which lets us write:

mysecondtable = map(star(list), mytable)

While we're here, we should also have the natural complement of star, its
evil mirror universe twin:

def bearded_star(f):
def bearded_starred_f(*args):
return f(args)
return bearded_starred_f

Better names (eg "unpacking" and "packing") would obviously be needed.

tom

--
I might feel irresponsible if you couldn't go almost anywhere and see
naked, aggressive political maneuvers in iteration, marinating in your
ideology of choice. That's simply not the case. -- Tycho Brahae

Paul Rubin

unread,
Jan 21, 2006, 3:08:24 PM1/21/06
to
Tom Anderson <tw...@urchin.earth.li> writes:
> > listx/dictx/setx would be the display forms as well as the constructor forms.
>
> Could these even replace the current forms? If you want the equivalent
> of list(sometuple), write list(*sometuple).

The current list function is supposed to be something like a typecast:

list() = []
xlist() = [] # ok

list(list()) = [] # casting a list to a list does nothing
xlist(xlist()) = [[]] # make a new list, not the same

list(xrange(4)) = [0,1,2,3]
xlist(xrange(4)) = [xrange(4)] # not the same

list((1,2)) = [1,2]
xlist((1,2)) = [(1,2)]

etc.

Tom Anderson

unread,
Jan 21, 2006, 3:45:42 PM1/21/06
to
On Sat, 21 Jan 2006, it was written:

> Tom Anderson <tw...@urchin.earth.li> writes:
>
>>> listx/dictx/setx would be the display forms as well as the constructor
>>> forms.
>>
>> Could these even replace the current forms? If you want the equivalent
>> of list(sometuple), write list(*sometuple).
>
> The current list function is supposed to be something like a typecast:

A what?

;-|

> list() = []
> xlist() = [] # ok
>
> list(list()) = [] # casting a list to a list does nothing
> xlist(xlist()) = [[]] # make a new list, not the same
>
> list(xrange(4)) = [0,1,2,3]
> xlist(xrange(4)) = [xrange(4)] # not the same
>
> list((1,2)) = [1,2]
> xlist((1,2)) = [(1,2)]

True, but so what? Is it that it has to be that way, or is it just that it
happens to be that way now?

tom

--
It's the 21st century, man - we rue _minutes_. -- Benjamin Rosenbaum

Steve Holden

unread,
Jan 22, 2006, 6:07:33 AM1/22/06
to pytho...@python.org
Paul Rubin wrote:
> Tom Anderson <tw...@urchin.earth.li> writes:
>
>>>listx/dictx/setx would be the display forms as well as the constructor forms.
>>
>>Could these even replace the current forms? If you want the equivalent
>>of list(sometuple), write list(*sometuple).
>
>
> The current list function is supposed to be something like a typecast:
>
list() isn't a function, it's a type.

>>> type(list)
<type 'type'>

I'm not happy about the way the documentation represents types as
functions, as this obscures the whole essence of Python's object
orientation.


> list() = []
> xlist() = [] # ok
>
> list(list()) = [] # casting a list to a list does nothing
> xlist(xlist()) = [[]] # make a new list, not the same
>
> list(xrange(4)) = [0,1,2,3]
> xlist(xrange(4)) = [xrange(4)] # not the same
>
> list((1,2)) = [1,2]
> xlist((1,2)) = [(1,2)]
>
> etc.

I presume that here "=" means "evaluates to"?

regards
Steve
--
Steve Holden +44 150 684 7255 +1 800 494 3119
Holden Web LLC www.holdenweb.com
PyCon TX 2006 www.python.org/pycon/

Christoph Zwerschke

unread,
Jan 22, 2006, 7:26:54 PM1/22/06
to
Alex Martelli wrote:
>>>> print set([1,2,3])
> set([1, 2, 3])
>
> input and output could be identical. Do YOU have any good reason why
> sets should print out as set(...) and lists should NOT print out as
> list(...)? Is 'list' somehow "deeper" than 'set', to deserve a special
> display-form syntax which 'set' doesn't get? Or are you enshrining a
> historical accident to the level of an erroneously assumed principle?

These are valid points, but they lead me to the opposite conclusion: Why
not let {a,b,c} stand for set([a,b,c])? That would be very intuitive
since it is the mathematical notation already and since it resembles the
notation of dictionaries which are similar to sets.

(This has been probably discussed already. One problem I'm already
seeing is that {} would be ambiguous.)

Anyway, I think the fact that the notation for a set is clumsy is no
good reason to make the notation for a list clumsy as well.

-- Christoph

Paul Rubin

unread,
Jan 22, 2006, 7:40:48 PM1/22/06
to
Steve Holden <st...@holdenweb.com> writes:
> > The current list function is supposed to be something like a
> > typecast:
> >
> list() isn't a function, it's a type.

I'm not sure what the distinction is supposed to be. "list" is anyway
callable, and lambda a:list(a) is certainly a function.

> > xlist((1,2)) = [(1,2)]
> > etc.
>
> I presume that here "=" means "evaluates to"?

Yeah, although I meant something more informal, like mathematical
equivalence.

Maybe the preferred spellings for the constructors would use capital
letters: List, Dict, Set, instead of listx or xlist or whatever. That
would break the current meaning of Set but I hope not much depends on
that yet.

Alex Martelli

unread,
Jan 22, 2006, 8:05:14 PM1/22/06
to
Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

> Steve Holden <st...@holdenweb.com> writes:
> > > The current list function is supposed to be something like a
> > > typecast:
> > >
> > list() isn't a function, it's a type.
>
> I'm not sure what the distinction is supposed to be. "list" is anyway

You can subclass a type, you can check for it with isinstance, etc, all
things you couldn't do if list was still a factory function as in 2.1
and back.


Alex

Alex Martelli

unread,
Jan 22, 2006, 8:05:14 PM1/22/06
to
Christoph Zwerschke <ci...@online.de> wrote:
...

> These are valid points, but they lead me to the opposite conclusion: Why
> not let {a,b,c} stand for set([a,b,c])? That would be very intuitive

As syntax sugar goes, that would be on a par with the current "dict
display" notation, at least.

> (This has been probably discussed already. One problem I'm already
> seeing is that {} would be ambiguous.)

Yep, using {} for both sets and dicts wouldn't be a good idea. I
suspect most core Python developers think of dicts as more fundamental
than sets, so... (I may disagree, but I just don't care enough about
such syntax sugar to consider even starting a debate about it on
python-dev, particularly knowing it would fail anyway).

> Anyway, I think the fact that the notation for a set is clumsy is no
> good reason to make the notation for a list clumsy as well.

I don't agree that <typename>(<arguments>) is a clumsy notation, in
general; rather, I consider "clumsy" much of the syntax sugar that is
traditional in Python. For example, making a shallow copy of a list L
with L[:] is what strikes me as clumsy -- list(L) is SO much better.
And I vastly prefer dict(a=1,b=2) over the clumsy {'a':1, 'b':2}.

I suspect I'm unusual in that being deeply familiar with some notation
and perfectly used to it does NOT necessarily make me LIKE that
notation, nor does it make me any less disposed to critical reappraisal
of it -- the brains of most people do appear to equate habit with
appreciation. In the light of my continuous and unceasing critical
reappraisal of Python's syntax choices, I am quite convinced that many
of them are really brilliant -- with the "display forms" of some
built-in types being one area where I find an unusually high density of
non-brilliance, AKA clumsiness. But, that's just me.


Alex

Christoph Zwerschke

unread,
Jan 22, 2006, 8:30:50 PM1/22/06
to
Alex Martelli wrote:
> Yep, using {} for both sets and dicts wouldn't be a good idea. I
> suspect most core Python developers think of dicts as more fundamental
> than sets, so... (I may disagree, but I just don't care enough about
> such syntax sugar to consider even starting a debate about it on
> python-dev, particularly knowing it would fail anyway).

I'm still not convinced. At least I'd prefer {a,b,c} over any other
proposed solutions (http://wiki.python.org/moin/Python3%2e0Suggestions)
such as <a,b,c> or |a,b,c|.

You can argue that the notation for sets can be clumsy because they
aren't used so much as lists or dicts, but you can also argue the other
way around that sets aren't used much because the notation is clumsy
(and because they didn't exist from the beginning).

For instance, if sets had a simple notation, they could be used in more
cases, e.g. replace integer masks (see again
http://wiki.python.org/moin/Python3%2e0Suggestions):

pat = re.compile("some pattern", re.I|re.S|re.X)
would become
pat = re.compile("some pattern", {re.I, re.S, re.X})

> I don't agree that <typename>(<arguments>) is a clumsy notation, in
> general; rather, I consider "clumsy" much of the syntax sugar that is
> traditional in Python.

If you really could write list(a,b,c) instead of list((a,b,c)) I do
somewhat agree.

> For example, making a shallow copy of a list L
> with L[:] is what strikes me as clumsy -- list(L) is SO much better.

Certainly.

> And I vastly prefer dict(a=1,b=2) over the clumsy {'a':1, 'b':2}.

Ok, but only as long as you have decent keys...

> I suspect I'm unusual in that being deeply familiar with some notation
> and perfectly used to it does NOT necessarily make me LIKE that
> notation, nor does it make me any less disposed to critical reappraisal
> of it -- the brains of most people do appear to equate habit with
> appreciation. In the light of my continuous and unceasing critical
> reappraisal of Python's syntax choices, I am quite convinced that many
> of them are really brilliant -- with the "display forms" of some
> built-in types being one area where I find an unusually high density of
> non-brilliance, AKA clumsiness. But, that's just me.

Ordinary people are lazy. If we have learned something and got
accustomed to it, we don't want to relearn. It is inconvenient. And
there is this attitude: "If I had so much trouble learning a clumsy
notation, why should future generations have it easier."

And in programming languages, you also have the downward compatibility
problem. Having to change all your old programs makes people even more
dislike any thoughts about changes...

-- Christoph

Steven D'Aprano

unread,
Jan 23, 2006, 5:43:16 AM1/23/06
to
On Sun, 22 Jan 2006 16:40:48 -0800, Paul Rubin wrote:

> Steve Holden <st...@holdenweb.com> writes:
>> > The current list function is supposed to be something like a
>> > typecast:
>> >
>> list() isn't a function, it's a type.
>
> I'm not sure what the distinction is supposed to be. "list" is anyway
> callable, and lambda a:list(a) is certainly a function.


class Parrot:
def __init__(self):
pass

Parrot is callable. Is it a function?


Types are types, classes are classes, functions are functions.

Admittedly I still confused between the various flavours of functions
(function, bound method, unbound method, class method, static method...)
*wink* but the difference between types and functions is fairly clear.

Just don't ask about the difference between type and class... *wink*

--
Steven.

Bengt Richter

unread,
Jan 24, 2006, 12:02:29 AM1/24/06
to
On Mon, 23 Jan 2006 21:43:16 +1100, Steven D'Aprano <st...@REMOVETHIScyber.com.au> wrote:

>On Sun, 22 Jan 2006 16:40:48 -0800, Paul Rubin wrote:
>
>> Steve Holden <st...@holdenweb.com> writes:
>>> > The current list function is supposed to be something like a
>>> > typecast:
>>> >
>>> list() isn't a function, it's a type.
>>
>> I'm not sure what the distinction is supposed to be. "list" is anyway
>> callable, and lambda a:list(a) is certainly a function.
>
>
>class Parrot:
> def __init__(self):
> pass
>
>Parrot is callable. Is it a function?
>

No. It is an object that inherits a __call__ method that makes it callable with a (<args>) source syntax trailer
in a similar way to an object created with a def (which is a function in python convention, and which
also inherits a __call__ method), but the similarity does not make Parrot a function:

>>> class Parrot:
... def __init__(self):
... pass
...
(BTW you could have inherited a do-nothing __init__ ;-)

>>> type(Parrot).mro()
[<type 'classobj'>, <type 'object'>]
>>> type(Parrot).mro()[0].__call__
<slot wrapper '__call__' of 'classobj' objects>
Or, showing more explicitly where it comes from:
>>> type(Parrot).mro()[0].__dict__['__call__']
<slot wrapper '__call__' of 'classobj' objects>

Invoked:
>>> type(Parrot).mro()[0].__dict__['__call__'](Parrot)
<__main__.Parrot instance at 0x02EF340C>

Hm, actually that is like calling the im_func of the unbound method of a newstyle class ...
I think maybe actually Parrot() causes

>>> type(Parrot).mro()[0].__dict__['__call__'].__get__(Parrot, type(Parrot))()
<__main__.Parrot instance at 0x02EF398C>

A function is also an object with a __call__ method. E.g., compare above the line with
calling foo (after definition just below) the long way:

>>> def foo(): return 'returned by foo'
...
>>> type(foo).mro()[0].__dict__['__call__'].__get__(foo, type(foo))()
'returned by foo'

Playing with that a little:
>>> type(foo).mro()
[<type 'function'>, <type 'object'>]
>>> type(foo).mro()[0]
<type 'function'>
>>> type(foo).mro()[0].__call__
<slot wrapper '__call__' of 'function' objects>
>>> type(foo).mro()[0].__call__(foo)
'returned by foo'
>>> foo.__call__
<method-wrapper object at 0x02EF340C>
>>> foo.__call__()
'returned by foo'
>>> type(foo).mro()[0].__call__.__get__
<method-wrapper object at 0x02EF340C>
>>> type(foo).mro()[0].__call__.__get__(foo, type(foo))
<method-wrapper object at 0x02EF39AC>
>>> type(foo).mro()[0].__call__.__get__(foo, type(foo))()
'returned by foo'
or
>>> foo()
'returned by foo'

>
>Types are types, classes are classes, functions are functions.

classes seem to be classobjs, designed to implement classic class behavior
but using the new machinery to achieve compatible integration.

>
>Admittedly I still confused between the various flavours of functions
>(function, bound method, unbound method, class method, static method...)
>*wink* but the difference between types and functions is fairly clear.
>
>Just don't ask about the difference between type and class... *wink*
>

Why not? ;-)

Regards,
Bengt Richter

0 new messages