The fundamental problem is with a statement like this:
if x=5:
...
Did the user really mean to assign or did they just mean to test? Observe
that if they wanted to test, x must have already been defined. Therefore,
if x is not defined, this is safe.
If x is defined, then they could have meant either definition. So we have
an "ambiguity" (due to human fallability). In the case of this ambiguity,
Python should raise an error message:
OverwriteError: Assignment expression cannot overwrite existing value
If x=5 is what you really meant to do, then you can code it like this:
if x1=5:
x=x1
...
...
The very first time a newbie runs into the error they will wonder why
having assignment expressions overwrite values is dangerous.
Paul Prescod - ISOGEN Consulting Engineer speaking for only himself
http://itrc.uwaterloo.ca/~papresco
To me programming is more than an important practical art. It is
also a gigantic undertaking in the foundations of knowledge.
- Grace Hopper
Very cute, but doesn't really help for the common idiom:
while line = sys.stdin.readline():
"...process line..."
because the second time around the loop line will be defined already.
Adding a "del line" at the end is just as ugly.
My vote is still for (1) introduce := as an alternative to =, or (2)
require parentheses around an assignment used in an expression
context.
--Guido van Rossum (home page: http://www.python.org/~guido/)
Given that parentheses are so often unnecessary in Python, I strongly
argue against 'magically' boosting their power. I think that making some
parentheses 'powerful' and not others (e.g. the (a) vs. (a,) thang) is a
bad idea, likely to lead to lots of confusion.
I like using the mathematical/Pascal := as an assignment operator. In
fact, for Python 2, I suggest getting rid of the ambiguous '=' operator
altogether, and making people choose between := and ==.
--d 'never thought I'd voice an opinion in this thread. Go figure' a
Even though I'm not in love with ":=", it seems clearly preferable to
parentheses for making the programmers intent obvious.
Since the capability is certainly desirable enough to overcome my
minor aversion to the spelling I'd vote for ":=" too. But since when
has this been a democracy, oh benignly dictatorial one? ;-) ;-) ;-)
(all hail the once and future Guido!)
--
Under US Code Title 47, Sec.227(b)(1)(C), Sec.227(a)(2)(B) This email
address may not be added to any commercial mail list with out my permission.
Violation of my privacy with advertising or SPAM will result in a suit for a
MINIMUM of $500 damages/incident, $1500 for repeats.
Guido> My vote is still for (1) introduce := as an alternative to =, or
Guido> (2) require parentheses around an assignment used in an
Guido> expression context.
How about: (3) leave it the way it is. Python is already perfect. Is there
no chance that things remain as they are? Is there no "silent majority"
that could make itself heard???
M.
Jeez, do you have to ask? They don't spell "assignment" in any language.
I agree. (1) seems nicer to me. There are too many places where
parentheses don't change the meaning of an expression to start
introducing places where they do. (I remember a quote from a book,
something about a language (Perl, VB?) requiring parens in some
places, disallowing them in others, changing the meaning in still
others, and never telling you what to do in any given circumstance.)
Just for the record, Guido, what was the objection to "$" or "@"
instead of ":="?
> Since the capability is certainly desirable enough to overcome my
> minor aversion to the spelling I'd vote for ":=" too. But since when
> has this been a democracy, oh benignly dictatorial one? ;-) ;-) ;-)
It's always been a democracy, as long as we all agree with Guido.
(You get one vote, to be used for the candidate of our choice. :)
Later,
Blake.
> Guido> My vote is still for (1) introduce := as an alternative to =, or
> Guido> (2) require parentheses around an assignment used in an
> Guido> expression context.
>How about: (3) leave it the way it is. Python is already perfect. Is there
>no chance that things remain as they are?
All of the options discussed so far have been carefully crafted to not break
existing code, or change in any way the superior Pythonic coding experience.
You may rest assured that your hundreds, if not thousands of dollars have
been, if not spent wisely, at least squandered without overt deletorious
effect.
>Is there no "silent majority" that could make itself heard???
Certainly not if they reamin silent.
>M.
--
-William "Billy" Tanksley
"How could [Y2K] be a problem in a country where we have Intel and Microsoft?"
-Al "Forrest" Gore
DA> Given that parentheses are so often unnecessary in Python, I
DA> strongly argue against 'magically' boosting their power. I
DA> think that making some parentheses 'powerful' and not others
DA> (e.g. the (a) vs. (a,) thang) is a bad idea, likely to lead to
DA> lots of confusion.
OTOH, parentheses are often used in the condition part when the test
spans multiple lines (as opposed to backslash continuations). Not
exactly analogous, and I'm not sure which way that tips the balance,
if at all.
-Barry
> I'm sure that you have assignment expression fatigue as I do, but I've had
> a new (to me, at least) idea. I think it will work and it requires no new
> syntax beyond making assignment an expression. No new or overloaded
> keywords.
>
> The fundamental problem is with a statement like this:
>
> if x=5:
> ...
>
> Did the user really mean to assign or did they just mean to test? Observe
> that if they wanted to test, x must have already been defined. Therefore,
> if x is not defined, this is safe.
>
> If x is defined, then they could have meant either definition. So we have
> an "ambiguity" (due to human fallability). In the case of this ambiguity,
> Python should raise an error message:
>
> OverwriteError: Assignment expression cannot overwrite existing value
Hm. This might be an elegant way of solving the problem "make
assignment expressions safe," for some value of "safe" and some
definition of "defined." Unfortunately it doesn't appear to solve the
original problems, at least without extensive use of "del":
del line # just to be safe
while line = sys.stdin.readline():
print line,
del line
Is this really an improvement?
--
Mark Jackson - http://www.alumni.caltech.edu/~mjackson
One of the symptoms of an approaching nervous breakdown
is the belief that one's work is terribly important.
- Bertrand Russell
> All of the options discussed so far have been carefully crafted to not break
> existing code, or change in any way the superior Pythonic coding experience.
> You may rest assured that your hundreds, if not thousands of dollars have
> been, if not spent wisely, at least squandered without overt deletorious
> effect.
I don't want to use a language in which
if (x = 5)
or if x = 5
might be an intended assignment or a stupid typo. (In C or C++, when I'm
stuck using those for some reason, I now tend to write
if (5 == x)
so that the typo produces an error about trying to change a constant
(better than nothing, when one side is a constant).
One of the things that attracted me to Python was exactly that there is no
such problem in Python. [And, as a bonus, no chance to write a numeric
equality test rather than a string equality test accidentally, as in
Perl.]
If assignment as expression can be introduced with NO chance that my
missing = won't be caught by the compiler, I won't complain too loudly.
The := seems to meet that "requirement", since I never have to use it, and
if I do try and leave off the : the compiler notices.
[I've never actually needed assignment as expression, although it is handy
every year or so.]
My goal isn't to write the shortest possible code, but to write code I can
read, and which the compiler can help me get right. If I wanted to write
really compact code, I'd break down and learn John Iverson's joke on the
industry: APL.
--John
[Go ahead and flame...having responded to the thread once, I doubt I'll
read it again.]
--
If nothing is pressing, putter about with this or that.
(Fortune cookie)
John W. Baxter Port Ludlow, WA USA j...@olympus.net
Using parentheses is preferable.
while (line = sys.stdin.readline()):
"...process line..."
No additional operator which may be misspelled/missunderstood
and close to the common use of parentheses in expressions and
functions.
- Peter
Don't be so hard on him, Guido. He is a poor soul obviously damaged by
exposure to a certain programming language that shall remain
nameless... (grin)
Starting-to-get-the-gist-of-this-bizarre-adverbial-chain-thing-ly
yours,
John
Does this mean:
= does not work in expressions
:= does work in expressions
Does := work outside expressions?
> or (2)
> require parentheses around an assignment used in an expression
> context.
Ick. C programmers going into Python and writing this:
while (foo=bar):
...
would be fine now (they'd get an error). But they would get weird subtle
bugs if you do (2). I don't much like parenthesis having a semantic side
effect like this. Parenthesis in expressions are in my opinion for
disambiguation of human readable expressions, so that computers can
understand them. Not for making something work that otherwise wouldn't.
So if these are the options I'd pick (1), which at least is somewhat
less confusing.
But-a-language-with-two-assignment-operators-is-confusing-too-ly yours,
Martijn
Hi,
I didn't read the thread about assignments from the beginning.
I'm also not a language lawyer. So maybe I miss something.
But anyway ...
How about the following:
while <boolean expression> <keyword> <assignment>:
do_something()
where '<keyword> <assignment>' is optional. Obviously, the
assignment is executed before the boolean expression
is evaluated.
This would also be compatible to current Python (except for
the new keyword).
Example (where the keyword is 'after'):
while line after line = sys.stdin.readline():
print line
I think, something that preserves the pseudo-code-like nature
of Python would be the best. That's why I wouldn't prefer ':='.
Maybe I accept '===' which is assignment '=' and comparison '=='
:o)
Udo
--
(_(_(__)_)_) >--{ working on the FAMOOS project
_oo ~ http://dis.sema.es/projects/FAMOOS }--.
(___/\ |
`----{ Udo Gleich }--{ phoneto:+49-731-505-4810 }--'
> Guido van Rossum wrote:
> > My vote is still for (1) introduce := as an alternative to =, or (2)
> > require parentheses around an used in an expression
> > context.
>
> Hi,
>
> I didn't read the thread about assignments from the beginning.
> I'm also not a language lawyer. So maybe I miss something.
Well, you missed the point that added keywords are 100% out. (I've
tried my share... ;)
> But anyway ...
>
> How about the following:
>
> while <boolean expression> <keyword> <assignment>:
> do_something()
>
[...]
>
> This would also be compatible to current Python (except for
> the new keyword).
Exactly...
Another spelling suggested by myself and others, that would be
compatible without any new keywords (and wouldn't break any code, as
far as I can see, since this sort of thing isn't legal yet) is:
while (line = sys.stdin.readline(); line):
print line
>
> Example (where the keyword is 'after'):
>
> while line after line = sys.stdin.readline():
> print line
>
> I think, something that preserves the pseudo-code-like nature
> of Python would be the best. That's why I wouldn't prefer ':='.
> Maybe I accept '===' which is assignment '=' and comparison '=='
I kinda like === too... ;)
But isn't the statement/expression thing w/semicolons more general?
> :o)
>
>
> Udo
>
> --
> (_(_(__)_)_) >--{ working on the FAMOOS project
> _oo ~ http://dis.sema.es/projects/FAMOOS }--.
> (___/\ |
> `----{ Udo Gleich }--{ phoneto:+49-731-505-4810 }--'
--
--
Magnus
Lie
Hetland www.pvv.org/arcadia <arc...@pvv.org>
By the way, *why* are new keywords out? I hear everybody say that. I
hear people say Guido doesn't want them. But let me be the naive newbie
and ask why. :)
I don't much like overloaded keywords, so I can see why they're out. I
am not very enchanted by a reuse of 'for' or 'from' to mark assignments
in expressions. But new keywords in some contexts wouldn't seem to break
too much code, and would be much more clear than most expression magic
I've seen tossed around. You could even do a simple search and replace
if you're afraid the keyword is already in use in other code.
I can see why we don't want a whole slew of new keywords, but why not
one or two judicious new ones? Especially if this is slated for Python 2
as opposed to Python 1.6; I can see Python 1.6 might need a more
backwards compatible solution, but then again, I don't see this problem
as urgent enough. Can't we all wait until 2001 or whenever Python 2 will
be and do without these assignments in expressions until then? :)
'while 1'-is-not-that-horrible-and-weird-'if'-nesting-I-haven't-
suffered-from-yet-ly yours,
Martijn
Correct.
> := does work in expressions
Correct.
> Does := work outside expressions?
Correct.
> > or (2)
> > require parentheses around an assignment used in an expression
> > context.
>
> Ick. C programmers going into Python and writing this:
>
> while (foo=bar):
> ...
>
> would be fine now (they'd get an error). But they would get weird subtle
> bugs if you do (2). I don't much like parenthesis having a semantic side
> effect like this. Parenthesis in expressions are in my opinion for
> disambiguation of human readable expressions, so that computers can
> understand them. Not for making something work that otherwise wouldn't.
>
> So if these are the options I'd pick (1), which at least is somewhat
> less confusing.
Which is why I voted for that second.
> But-a-language-with-two-assignment-operators-is-confusing-too-ly yours,
We would have to adopt Perl's motto, "there's more than one way to do
it" :-)
For those of us coming from c, this makes line look like a statement after
the semicolon.
My $0.02: (note that these are my OPINIONS, and are worth what you spent on
them)
Assignments in conditionals are bad. The only reason to do it is to shrink
your code (good goal). The cost is usually readability AND unintentional
errors. Having an assignment in a conditional is a side effect, and can
cost someone a lot of time to track down. I know some are suggesting ':=',
and I could see that as a possibility, but really don't see the need for
that when other needs are so great. Let's try to add real meat to the
language instead of minor changes as to the placement of comma's or such.
How about:
Binary numbers: 0b01010101
first() and last() for sequences
removefirst() and removelast() remove the first/last element of a sequence
(kind of the opposite of append(). Furthermore, have them return the
element so that post list removal processing can be done. More
optimizations can be done on this than remove(x) which can be anywhere on
the list.
switch style statement structure.
reserve(size) for sequence to give a clue how many of something we will need
(great if we can do sequences like stl vectors or arrays)
using(object) returns sequence for every object that has a reference to
object.
void Py_SystemReserveMemory(long amount, void *mem=NULL)
long Py_SystemMemoryAvaiable()
Improve error reporting so that if there is a switch between tabs and spaces
(mixing them) a proper error message reporting just that comes out.
Each of these solve fundamental needs. Let's not argue over irrelevant
detail and instead focus on doing amazing things with this language.
A while back I posted much the same opinion:
see http://x2.dejanews.com/getdoc.xp?AN=414836715
I still stand by the opinion expressed in that posting. Most
of the issues, such as assignment expression or integer division, that
cause so many electrons to be spilt are minor tweaks that don't add
much expressive power. It's not worth the disruption of adding a new
feature -- changing the docs, fixing broken code -- unless the feature
will give you 50% more functionality, and most of these changes only
add 1 or 2%. I'd simply forget them, call Python 1.x frozen, and
leave the extensive improvements for 2.0.
--
A.M. Kuchling http://starship.skyport.net/crew/amk/
Up the lane aways is the Inn. You just have to be *sure* it's there, though.
If you aren't sure, then fizzlywinks, it's only goin' to be fireflies and
treeses.
-- Giving directions, in SANDMAN #51: "A Tale of Two Cities"
jay:/gcm/jglascoe/BDB
$ ./combine_jims_okay.py
File "./combine_jims_okay.py", line 111
assert len(sys.argv) = 1, USAGE
^
SyntaxError: invalid syntax
just-wanna-say-i'd-be-screwed-many-times-over-if-assignment=expression'ly
y'rs,
Jay Glascoe
jgla...@jay.giss.nasa.gov
> Udo Gleich <gle...@dbag.ulm.daimlerbenz.com> writes:
>
>
> > Maybe I accept '===' which is assignment '=' and comparison '=='
>
> I kinda like === too... ;)
Me too, this proposal really struck me as elegant. It makes it much
more
difficult to miss accidentally than the ":=" option (I could easily miss
the
semi-colon, although I'm sure python-mode.el would save my butt), and it
just seems very elegant and authoritative ( line === file.readline() )
On the other hand, I would like this more if "=" mean equality test and
"==="
meant assignment, then there would be a two char difference between the
meanings.
As it is, it could be just as easily confused with "==" :-(.
Oh well, I never thought of this whole subject as being too much of a
problem.
The ability to use new variables without having to declare them make it
easy to
break down complex expressions into more readable and managable chunks;
this
advantage far outweighs any loss of flexibility that lack of assignment as
expression
brings (IMHO)...
Chad Netzer
ch...@vision.arc.nasa.gov
OK. Then add another '='
'===='
:oP
Udo
Obviously most people do not see this as irrelevant detail. It strikes me
as rather arrogant of you to sweep aside everyone else's needs in favour
of your own.
> Each of these solve fundamental needs.
I beg to differ:
>
> Binary numbers: 0b01010101
Solution one: use hex.
Solution two: write a ten line function to decode binary strings.
> first() and last() for sequences
sequence[0], sequence[-1]
> removefirst() and removelast() remove the first/last element of a sequence
del sequence[0]
del sequence[-1]
> (kind of the opposite of append(). Furthermore, have them return the
> element so that post list removal processing can be done.
x=sequence[0]; del sequence[0]
x=sequence[-1]; del sequence[-1]
> switch style statement structure.
Hardly any more "fundamental" than assignment in expression.
> reserve(size) for sequence to give a clue how many of something we will need
> (great if we can do sequences like stl vectors or arrays)
myvec = [None]*50
> using(object) returns sequence for every object that has a reference to
> object.
That is VERY hard to implement efficiently.
> void Py_SystemReserveMemory(long amount, void *mem=NULL)
> long Py_SystemMemoryAvaiable()
???
> Improve error reporting so that if there is a switch between tabs and spaces
> (mixing them) a proper error message reporting just that comes out.
python -t
On to your criticisms of the proposal:
> For those of us coming from c, this makes line look like a statement after
> the semicolon.
That misinterpretation is not particularly dangerous. Still, I wouldn't
mind a syntax that makes the expression more distinct from the statements
that precede it.
> Assignments in conditionals are bad. The only reason to do it is to shrink
> your code (good goal). The cost is usually readability AND unintentional
> errors. Having an assignment in a conditional is a side effect, and can
> cost someone a lot of time to track down.
That's why this thread has gone on so long. The goal is to find a syntax
that is NOT hard to track down. The semicolon delimited proposal seems to
meet that requirement. Furthermore, the LACK of assignments in conditional
can cause bugs. This has been pointed out in the past.
Paul Prescod - ISOGEN Consulting Engineer speaking for only himself
http://itrc.uwaterloo.ca/~papresco
So what if one dark midnight less than a year from now, millions of
computers around the world suddenly grind to a halt? My computer grinds
to a halt several times a day. ... [Forget Y2K] We're ignoring a much
bigger bug problem that's hiding, well, right under our noses. Call it
the Y-Does-My-Computer-Crash-Three-Times-A-Day Problem.
- http://www.upside.com/David_Futrelle/
Another possibility:
x = sequence.pop()
-Fred
--
Fred L. Drake, Jr. <fdr...@acm.org>
Corporation for National Research Initiatives
1895 Preston White Dr. Reston, VA 20191
> We would have to adopt Perl's motto, "there's more than one way to do
> it" :-)
Or at least switch from the current "There's only one way to do it" to
"There's probably no more than two ways to do it, unless you are Tim
Peters", which is rather tough to say, and even tougher to acronym.
but-i'm-sure-Tim-is-up-to-the-challenge-ly y'rs, Nathan
--
Nathan Sullivan nsu...@pacbell.net
-------------------------------------------------------------------------------
Heisenberg may have slept here
Jay (and everyone else who hasn't followed this thread since 1991 <wink>),
that's exactly the kind of problem every proposal on the table is busting
its spine to avoid (except for a few (one?) over-eager newbie proposals).
If *anyone* (named Guido, anyway) were willing to live with that, this would
have been put out of its misery long before most of the people on c.l.py
ever heard of Python.
with-just-a-little-more-luck-our-grandchildren-will-know-the-
joy-of-debating-this-too-ly y'rs - tim
Indeed you are, but it's still a bit strained, as the hints of
self-conscious apology for adverbial excess intensify the very excess for
which apology is proffered. Try to do it with less studied concern over
what decent people will think <wink>.
at-the-party-of-life-adverbs-spike-the-punch-ly y'rs - tim
Primarily because they break currently-working code. Secondarily because
they break all meta-tools that depend on knowing Python's set of keywords,
from editors to preprocessors. Books, docs, slides etc too. Mostly,
though, it's a great builtin excuse for Guido to squash things he doesn't
like <wink>.
> ...
> But new keywords in some contexts wouldn't seem to break too much code,
Bingo. Python's parser doesn't work that way: if it's a keyword in one
place, it's reserved everywhere. The determination is made very early on,
essentially part of tokenization, and can't be retracted later.
> ...
> Python 2 as opposed to Python 1.6; I can see Python 1.6 might need a more
> backwards compatible solution, but then again, I don't see this problem
> as urgent enough.
There are, by definition, no problems whatsoever in Python2 <wink>. There
is some real desire (at least on my part) to *try out* some of the ideas
that come up earlier, though. Especially those we're not sure are 100%
wins. If it turns out they suck in practice, we can drop them again in
Python2. You think I'm joking <wink>. Vegas odds-makers are still calling
it even whether something named "lambda" will appear in Python2 ... and
that's one of the only two keywords ever added to Python that still survive
(the other was "exec", which was a builtin function at first, and needed to
become a keyword instead for the same reasons that came up in *this* thread
for why no "builtin function" approach can work for embedded assignments).
no-matter-how-often-i-type-this-stuff-it's-always-fresh<wink>-ly y'rs - tim
What about allowing an optional "assignment only" suite between "while" and
the test condition. This suite will be executed before testing the condition
each time (ie, a "continue" will execute them). ie:
while [assignment; [...]] condition:
This would allow us to write:
while 1:
line = file.readline()
if not line:
break
do_stuff(line)
as:
while line = file.readline(); line:
do_stuff(line)
We've dragged the important stuff (the loop setup between the "while 1" and
the test, and the test itself) to the same line as the while. The *end* of the
"while" statement is still where the condition is, so it's consistent with the
vanilla version when scanning source code.
I expect it would have to be forced to be a semi-colon separated suite, as:
while line = file.readline()
blah = wibble(line)
some_other_thing:
is open to confusion (whereas the similar structure under a forced version:
while line = file.readline(); \
blah = wibble(line); \
some_other_thing:
... still keeps us looking for the condition because of the explicit line
continuations).
Importantly, we've *not* had to tinker with existing operators, have special
rules if existing operators are within parens, or add a new
similar-but-not-quite-the-same operator for assignment (all of which make me
a bit nervous).
Adding this to "if" will solve the re.match() problem also:
if m = x(); m == 1: print "first case"
elif m = y(); m == 2: print "second case"
else: print "neither"
Furthermore, it could be done now as this syntax is currently illegal.
Cheers, Kev.
--
These are my own personal opinions. I do not speak on anyone else's
behalf.
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own
[Nathan Sullivan]
> Or at least switch from the current "There's only one way to do it" to
> "There's probably no more than two ways to do it, unless you are Tim
> Peters", which is rather tough to say, and even tougher to acronym.
>
> but-i'm-sure-Tim-is-up-to-the-challenge-ly y'rs, Nathan
Nathan, my boy, over the years I've contributed more stupid workarounds to
the years-long stream of "how can I make an assignment during the test?"
whines than the entire Perl and Scheme communities combined <wink>. There
are many, many, many ways to do it already. And they all suck.
I don't even want to solve the problem -- I just want to get out of the
endless job of replying to the complaints. It would be different if the
replies left the complainers happy, but after most of the decade trying, I
finally decided the fault wasn't due to a 100% consistent failure on my part
to explain it clearly enough <wink>.
atan(1)-times-4-is-a-lame-way-to-spell-pi-ly y'rs - tim
How about (3): forget about it.
When I talk to people who have written and maintained
large amounts of Python code, they never complain about
the lack of an assignment operator. Methinks it's a newbie
thingie.
Over and out /F
"Unfortunately, those people who have nothing better
to do than post on the Internet all day long are rarely
the ones who have the most insights." - Jakob Nielsen
AMK> A while back I posted much the same opinion: see
AMK> http://x2.dejanews.com/getdoc.xp?AN=414836715
AMK> I still stand by the opinion expressed in that posting. Most
AMK> of the issues, such as assignment expression or integer
AMK> division, that cause so many electrons to be spilt are minor
AMK> tweaks that don't add much expressive power. It's not worth the
AMK> disruption of adding a new feature -- changing the docs, fixing
AMK> broken code -- unless the feature will give you 50% more
AMK> functionality, and most of these changes only add 1 or 2%. I'd
AMK> simply forget them, call Python 1.x frozen, and leave the
AMK> extensive improvements for 2.0.
I hate "me too" postings, but I am afraid to become part of the
silent majority. PLEASE, PLEASE do not make python a write-only
language?!
Rob
--
===== R.H...@EuroMail.net http://www.xs4all.nl/~hooft/rob/ =====
===== R&D, Nonius BV, Delft http://www.nonius.nl/ =====
===== PGPid 0xFA19277D ========================== Use Linux! =========
[other reasons snipped -- I knew about these but didn't see they were
THAT big a deal]
> Mostly,
> though, it's a great builtin excuse for Guido to squash things he doesn't
> like <wink>.
Okay, that's a better reason. :)
> > Python 2 as opposed to Python 1.6; I can see Python 1.6 might need a more
> > backwards compatible solution, but then again, I don't see this problem
> > as urgent enough.
>
> There are, by definition, no problems whatsoever in Python2 <wink>.
I know. Python 2 is utter perfection. :)
> There
> is some real desire (at least on my part) to *try out* some of the ideas
> that come up earlier, though. Especially those we're not sure are 100%
> wins. If it turns out they suck in practice, we can drop them again in
> Python2.
Agreed. But I'd think we'd try those out in Python2 prototypes instead
of in the 1.x line of Pythons, right? Unless you want a really large
scale testing of new features. I always imagined there'd be prototype
Python2 implementations first, which would be a testing ground for
various language features. But I'm probably completely wrong about this.
> You think I'm joking <wink>.
I didn't. But this line made me doubt. :)
Regards,
Martijn
And not just random newbies, but newbies who've been corrupted by
certain languages. Me, I'm a language slut; one language I'm using
(created less than three years ago) has no "while" loops and no
subroutines.
I know how tiring it can get to explain things over and over again (I
used to do tech support for that language), but has anyone presented
evidence that assignments in conditionals *ever* lead to clearer code in
the aggregate? (Note that "in the aggregate"; it's easy to contrive a
simple case.)
--
--- Aahz (@netcom.com)
Hugs and backrubs -- I break Rule 6 <*> -=> http://www.rahul.net/aahz
Androgynous poly kinky vanilla queer het
I don't have enough time for all the cool people I know, let alone all
the cool people *they* know.
The good thing of this is that it doen't allow atrocities like
x = (y = 1; z)
The bad thing is that it doesn't combine with short-circuit operators,
as in
while (x = f(); x) and (x.y = g(); x.y):
"whatever"
I don't think that only allowing semicolons inside parentheses is
particularly ugly.
:>My vote is still for (1) introduce := as an alternative to =, or (2)
:>require parentheses around an assignment used in an expression
:>context.
: How about (3): forget about it.
: When I talk to people who have written and maintained
: large amounts of Python code, they never complain about
: the lack of an assignment operator. Methinks it's a newbie
: thingie.
: Over and out /F
Aye, I'm in agreement. I _liked_ the idea that you could not have
assignment in an expression.
-Arcege
Indeed, this feature makes C code harder to follow. Some programmers
are really attracted to it - otherwise, of course, it wouldn't be much
of a problem. It's no great surprise that they'd want it in Python too.
Is it a newbie thing in the sense of ``experienced programmer new to Python'',
or in the sense of ``new to programming?'' I bet never the latter.
I'd guess total newbies are ill served by this feature.
Donn Cave, University Computing Services, University of Washington
do...@u.washington.edu
>
> while [assignment; [...]] condition:
[...]
> as:
>
> while line = file.readline(); line:
> do_stuff(line)
>
[...]
Nice...
> I expect it would have to be forced to be a semi-colon separated suite, as:
Probably... But that *could* be a matter of programming style, just as
using several statements separated by semi-colons is considered (at
least by me) as non-pretty elsewhere...
>
> while line = file.readline()
> blah = wibble(line)
> some_other_thing:
>
> is open to confusion
Hm... What kind of indent would we have afterwards?
while line = file.readline()
frooble()
quibble():
print x
Looks a bit strange, perhaps...
> (whereas the similar structure under a forced version:
>
> while line = file.readline(); \
> blah = wibble(line); \
> some_other_thing:
>
> ... still keeps us looking for the condition because of the explicit line
> continuations).
At least it is ugly enough to automatically discourage its use ;)
>
> Adding this to "if" will solve the re.match() problem also:
>
> if m = x(); m == 1: print "first case"
> elif m = y(); m == 2: print "second case"
> else: print "neither"
Maybe even in eval and lambda? (There it might be better to require
parens around the statement list
f = lambda x: line = readline; line
Then again, the syntax is already allowed there, except for the
assignment, so that might not be such a good idea...
Silent majority(?) member,
Manus
(Yeah, it would be kinda nice, but no, I don't like the solution.)
To me, it seems (finally) like a truly Python-like solution has
been suggested. If Guido expands his remarks of initial
approval, I for one would be VERY happy to accept
while x=something; x == something:
doSomething()
I also liked Guido's point that the problem of logical
operations can be addressed by considering this semicolon-
separated pair as a (making new terminology now) new kind
of super-tuple, if you will.
Basically, a super-tuple (which uses semicolons instead of colons
as its separator) would be akin to C's comma operator, I guess,
but Python would (could) add the restrictions that super-tuples
would be limited to len(2), and that the first element would be
required to be an assignment "statement" (or perhaps a <code>
object?? hmmm.)
With this, stuff (after Guido's example) like:
if (x = file.readline(); x) and (y = func(x); y == something):
works perfectly, and (to me) just LOOKS and reads like good
Python.
It preserves the clarity of each and every Python concept,
merely extending it with the syntactically unambiguous
super-tuple concept. Even someone who doesn't know Python
can see what this statement is doing.
I need to spend some time refining my thoughts on it, but
it seems to me that this concept fits the bill, and the more
I write about it, the more I like it.
And if there is some huge fatal flaw with the whole concept
that I am not seeing, I will unhesitatingly blame the lateness
of the hour of my post. :-)
(Just scanned the newsgroup to see if a fatal flaw has been
found, but I am only seeing more positive feedback so far,
so I'm going to go ahead and post. I like it.)
Manus
Why stop there? It could easily be any number of "small" statements
separated by semicolons with the restriction that the final one must
be an expression and that the ones before it must not be flow-control
statements (return, raise, continue, break). Other useful statements
to allow are print (for debugging) and assert, and for all I care
import, del, pass, and exec.
Note that I like this for 2.0, not so much for for 1.6 (if only
because I don't want 1.6 to make existing books obsolete).
yuk!
> > or (2) require parentheses around an assignment used in an
> > expression context.
ditto!
> How about (3): forget about it.
this gets my vote. amusing as this discussion is, i'm concerned about
uglifying the language to solve a non-problem. if i want terseness,
i have any number of other languages to choose from. instead,
i chose python.
--
Garry Hodgson rollin' down the highway
ga...@sage.att.com livin' in the spring
Software Innovation Services rainin' somewhere down the road
AT&T Labs now, that don't mean a thing
>
>Note that I like this for 2.0, not so much for for 1.6 (if only
>because I don't want 1.6 to make existing books obsolete).
Sounds great! If this means that this is officially "in", even if it's not
until 2.0, I'm a happy camper. I promise not to post any more on this topic
:-)
Style note: Personally I'd write long clauses thusly:
while \
spam = get_spam() ;\
print "Debug spam:", spam ;\
spam > 3:
# Sing
Mmmm... 2.0
Evan Simpson
Evan! While bribery is tolerated (even encouraged), this is blatant
extortion.
but-too-tempting-to-resist-ly y'rs
- Gordon
You suggested a syntax without the parentheses that I was
envisioning would enclose the semicolon-separated sequence of
"little" statements ending with an expression.
So I was about to bring up Guido's earlier messages saying
that the surrounding parentheses might be required on anything
that would do the job that is under discussion (this, not
coincidentally, would support my own interpretation of this
sequence of things as being a true Python object ["super-tuple"]
type in its own right).
But then, while writing this message, I realized that of course,
the parentheses would still be optional if it was a "super-tuple"
because of course they are already optional in regular
(comma-separated) tuples.
Still, even if they ARE optional (as I now think they should
be -- this is most consistent with the tuple paradigm that I
think fits real well here), I myself will consistently
parenthesize them if only to avoid the need to end each line
with a backslash.
And oh boy I look forward to doing it! Guido, what is the
release schedule for 2.0? :-)
Manus
> > For whatever my opinion is worth, I found myself really
[...]
>
> Why stop there? It could easily be any number of "small" statements
> separated by semicolons with the restriction that the final one must
> be an expression and that the ones before it must not be flow-control
> statements (return, raise, continue, break). Other useful statements
> to allow are print (for debugging) and assert, and for all I care
> import, del, pass, and exec.
Hooray!! A wonderful and Pythonesque solution. Full power and minimal
clobber/confusion.
(Sorry about the "mee too" post... I'm just so happy we finally got
somewhere with all this discussion. And I really like the solution too.)
>
> Note that I like this for 2.0, not so much for for 1.6 (if only
> because I don't want 1.6 to make existing books obsolete).
>
Seems reasonable enough.
> --Guido van Rossum (home page: http://www.python.org/~guido/)
--
Magnus
Lie
Hetland http://arcadia.laiv.org <arc...@laiv.org>
Now I wonder, if the construct is indeed a tuple-like piece of data (like
I say, I call it a "super-tuple"), will we be able to
do stuff like this:
x = (y = someFunc(); y)
while x:
if type(x) == type((0;0)):
print 'Just ran some function or other'
if y > 0: x = (y = someOtherFunc(); y)
else: x = 1
Call me crazy, but I sure HOPE we will!
And if so, will super-tuples be mutable? (My vote, which I
imagine will be most people's, is no.)
Manus
Hm. I'm not sure if this wouldn't be completely confusing. Still
pondering that.
But besides that, your code would (I think?) just be equivalent to this:
x = someFunc()
while x:
if type(x) == type(0): # and execute statement called '0' here.
syntax error?
print "Just ran some function or other'
if y > 0:
x = someOtherFunc()
else:
x = 1
Which isn't what you wanted, right?
The problem is of course that supertuples are evaluated; they aren't
themselves, like normal tuples are. This in itself is obviously
confusing, and I think we should think this through carefully.
Some options:
* Provide a way to dereference supertuples
I don't like this option. I'll be a special case only for supertuples,
and generally confusing.
* Normal supertuples aren't executed, they're like tuples in this sense
This'd be like this:
x = (y = foo(); y)
...
do(x) # or execute(x), or whatever. Compare with standard filter/map
functions.
I like this better, though I'm not sure what the exact implications are
with scoping, etc. That might be a can of worms. Then we'll add to this
can the worms coming from lambda, and we sure can fish for a long time.
:)
Then again, as Tim Peters expressed earlier, Python 2 will of course be
perfect. So I'm sure we'll resolve this issue in time. :)
Regards,
Martijn
as opposed to
x = y = someFunc()
Say, perhaps we can get rid of this special case if we add the
super-tuples...
>And if so, will super-tuples be mutable? (My vote, which I
>imagine will be most people's, is no.)
Heh heh heh... I can see the form of _my_ next entry in the Obfuscated
Not-Quite-Python contest... ;)
And if you combine mutability with the ability to delay execution, (the
"x = (y=someFunc();y); do(x)" suggestion from before... (or should
that be "x = (y=someFunc();y); z = do(x)" which could be shortened to
"z = do((y=someFunc();y))"?)), then you could get some _really_ messy
code.
Later,
Blake.
In my interpretation, the expression (S1; S2; ...; Sn; E) yields the
value of expression E -- S1-Sn are only there for their side effects.
So "super-tuple" (a name I don't like much :-) is not a new type --
it's a syntactic flow control construct, just like "and" or "if".
> Guido asked why stop at limiting what I have been calling a
> "super-tuple" to len(2).
I thought we were talking about the limited version, like:
while something(); something_else(); expression:
do_something()
Was I mistaken? Did Guido approve of the super-tuple?
>
> Manus
>Was I mistaken? Did Guido approve of the super-tuple?
No he:
1) Asked that it not be called "super tuple" because that's a very bad
name for it. It is NOT a tuple, doesn't act like a tuple, and only
very superfically resembles a tuple. As he points out it's really a
Python version of the C "comma operator". (if you insist on a name
how about "semicolon operator" or "compound expression".)
2) Said he like the idea, possibly for Python 2.0. This was NOT a
commitment as far as I could tell. If someone was feeling ambitious
they could implement it, and report on it at the next conference to
provide prior art precedent in the language. (Hey we tried this, and
it really helps a lot, and doesn't seem to cause any problems, or
gross too many people out...)
Manus
Since this is a long posting, I'll give a little contents:
1) a brief technique for so-called super-tuples to be lazy-evaluation
(needed for use as some think it should work)
2) a number of reason why I think this construct is a BAD idea
3) what could be done to solve ppl's grips and fix the "==" versus
"=" objections
And now for something not-at-all different:
Not that I like this idea - I think having: statement;statement;expr
is pretty un-pythonesque.
But how I envision it working is to create a code block which could
then be eval'd on __nonzero__(). Something like (but MUCH better and
more efficient *hehe*):
class BadlyNamedSuperTuple:
def __init__(self, *statements):
if len(statements) < 1:
raise ValueError, "throw me a bone, give me something to call"
self.statements = []
for s in statments[:-1]:
self.statements.append( compile(s, '<supertuple>', 'exec') )
self.expr = compile(statements[-1], '<supertuple>', 'eval')
def __nonzero__(self):
calling_frame = GetCallingFunctionFrame()
globals = calling_frame.fb_globals
locals = calling_frame.fb_locals
for s in self.statements:
exec s in globals, locals)
return eval(self.expr, globals, locals)
f = BadlyNamedSuperTuple( 'x = x - 1', 'x > 0' )
x = 10
while f:
print x
(The "GetCallingFunctionFrame" has been published enough on the newsgroup
even by myself, so I'll leave that to the reader :) This only works (as
is) if the variables are globals. And I'm sure that I have something
wrong, but I think it works.
But about the only thing this saves is the class and the string quoting.
I see the statement;statement;expression syntax (with or without the
parens) as being complicated and ugly [Well, I didn't expect the
Spanish Inquisition]:
* it is mixing compile types, which has been a no-no - eval() that can
run an 'exec' suite? Come now.
* it is departing from a very good language restriction: No assignments
inside an expression (the "super-tuple" on the whole is still an
expression). Adding parentheses added _no_ value to the age-old
problem of "=" vs. "==": the expression "(age == 10; age)" isn't too
visually different from "(age = 10; age)" and it is still the same
class of error (just reversed) with the same support problems.
* this confuses the issue about what is a statement, which is clear for
ppl learning the language to understand (does this push the average
time for understanding Python from a half hour to much longer... how
long is the average time for learning Perl? somewhere between 2 and 48
hours, I've heard in some circles). Python has been touted as being
an easy language to learn, and some ppl on this newsgroup have even
started teaching it to grade school children. Should we scrap that?
* what about other statements inside this construct and the related
whitespace? Is this now a suite or just a stmt_list (cf. Reference
Manual section 7. Compound statements)? A suites, although not able to
have a clause header, are able to be seperated by NEWLINE INDENT
tokens. Is allowed in this new suite construct? Do we now add
something other than a suite? And how does a stmt_list or suite
restrict the simple_stmt syntax (cf. section 6)?
* does every suite return the last value now? is the really nothing
more than the comma operator? And here I thought Python was relatively
context free (I did say relatively).
I seem to remember that just after the Dallas Conference, there was
talk of work for Python2 on some "get scope" function (like making the
assignment into a specialized function), where you could retrieve and
set unbound variables (maybe in a different scope).
Has anyone thought about something like:
x = 10
while assign('x', x-1) or x:
print x
def assign(name, expr, glob_dict=None, locl_dict=None):
if glob_dict is Nont:
glob_dict = GetScope('global', uplevel=1)
if locl_dict is None:
locl_dict = GetScope('local', uplevel=1)
if locl_dict.has_key(name):
locl_dict[name] = expr
else:
glob_dict[name] = expr
Or is this too crazy that it would work (so why do it)?? We already
have string based bound assignment (setattr), why not a string based
unbound assignemnt? It is even possible to piggy-back the two:
getattr(obj, name, value) - bound
getattr(None, name, value) - unbound (find the namespace)
I think it would serve a far greater end to work on a consistant
scoping mechanism than to add a "bad" syntax borrowed from languages
with known problems just for ppl who think they need it. Ni!
-Arcege
--
------------------------------------------------------------------------
| Michael P. Reilly | Email: arc...@shore.net |
| Software Configuration Manager | Email: mr...@gte.com |
------------------------------------------------------------------------
I tend to agree with Mr. Reilly's observations.
Besides, colons and semicolons are looking more and more alike as the years go
by. (I suppose I should find a better font...)
--
Eric Stechmann (+1) 612-785-2000 (voice)
Provis Corporation (+1) 612-785-2100 (facsimile)
5251 Program Avenue, Suite 100 e...@provis.com (e-mail)
Mounds View, MN 55112-4975 http://www.provis.com
(Subject to change w/o notice)
The box said 'Requires Windows 9*, or better.' So I bought a Macintosh.
Trying to stop people from using "==" when they meant "=" is expanding the
scope. People do that in Python *today*. Or at least one person did. I
spotted it immediately but the individual had been struggling with it for
a while.
a==i
for j in range( a ):
...
On your proposal:
> while assign('x', x-1) or x:
Ugh! Setattr accepts a string because that's what makes it useful.
Inventing a new syntax that will ALWAYS be used with quotes is just ugly.
And if it isn't always used with quotes then it adds a nasty new
enticement to make the logic of your code impenetrable. People will be
inventing local variable names based on URLs and other atrocities.
The semicolon proposal is not beautiful but it seems better to me. Maybe
we are trying to solve too many problems in one stroke. I'm disappointed
that we haven't found anything elegant yet.
--
Paul Prescod - ISOGEN Consulting Engineer speaking for only himself
http://itrc.uwaterloo.ca/~papresco
"Remember, Ginger Rogers did everything that Fred Astaire did,
but she did it backwards and in high heels."
--Faith Whittlesey
I've gotten the impression that Python is supposed to be an elegant
language. If there isn't an elegant solution to this problem, maybe
it's a sign that the language should not be changed.
But the problem isn't particularly elegant either.
And all of the workarounds I've seen have been decidedly non-elegant.
Later,
Blake.
> Trying to stop people from using "==" when they meant "=" is expanding the
> scope. People do that in Python *today*. Or at least one person did. I
> spotted it immediately but the individual had been struggling with it for
> a while.
>
> a==i
> for j in range( a ):
> ...
I guess this problem excludes the following style - my apologies if this
was already mentioned in this huge thread.
class CmpAssign :
def __cmp__(self,other) :
self.value = other
if other : return 0
else : return 1
def __call__(self,value) :
self.value = value
return value
def test() :
from StringIO import StringIO
fd = StringIO("one\ntwo\nthree\nfour\nfive\n")
line = CmpAssign()
print "test 1 : a loop with 'assignment'"
while line == fd.readline() :
print line.value,
fd.seek(0)
print "\ntest 2 : chaining of comparisons gives a bit of flexibility"
while line == fd.readline() != 'five\n' :
print line.value,
fd.seek(0)
print "\ntest 3 : and call syntax can be used for other cases"
while 'o' in line(value=fd.readline()) :
print line.value
>>> test()
test 1 : a loop with 'assignment'
one
two
three
four
five
test 2 : chaining of comparisons gives a bit of flexibility
one
two
three
four
test 3 : and call syntax can be used for other cases
one
two
>>>
Alternatively, we haven't generalized the problem *enough* and that's
why we can't find an elegant solution. In programming both strategies
have their place.
Not-that-I-have-any-clue-on-how-to-generalize-this-ly yours,
Martijn
GvR> Why stop there? It could easily be any number of "small" statements
GvR> separated by semicolons with the restriction that the final one must
GvR> be an expression and that the ones before it must not be flow-control
GvR> statements (return, raise, continue, break).
A function invocation is allowed? What happens if the called
function raises an exception?
I like the basic idea to introduce a syntactic structure that
let you write a series of statements which act as a term in an
expression, though.
Yes, but it would be exactly equivalent to today's:
x = y = someFunc()
while x:
if type(x) == type(0):
print 'Just ran some function or other'
if y > 0: x = y = someOtherFunc()
else: x = 1
> Call me crazy, but I sure HOPE we will!
Well, yes, you're crazy -- but it appears not for the reason you feared
<wink>.
a-sure-sign-of-insanity-if-ever-there-was-one-ly y'rs - tim
Assuming that syntax would be extended to support
assignment in expressions, this suggests :
while line(=fd.readline()) :
print line
e.g. <identifier>(=<expression>)
sets <identifier> to the value of <expression>, and
returns that value.
BB
> >
> > while 'o' in line(value=fd.readline()) :
> > print line.value
> >
>
> Assuming that syntax would be extended to support
> assignment in expressions, this suggests :
>
> while line(=fd.readline()) :
> print line
This seems a lot like Evan Simpson's class Pita...
http://www.dejanews.com/getdoc.xp?AN=429491196
There it would be:
line = Pita()
while 'o' in line(fd.readline()):
print line()
I think this thread is ready to die...
>
> BB
> >
> > while 'o' in line(value=fd.readline()) :
> > print line.value
> >
>
> Assuming that syntax would be extended to support
> assignment in expressions, this suggests :
>
> while line(=fd.readline()) :
> print line
>
> e.g. <identifier>(=<expression>)
>
> sets <identifier> to the value of <expression>, and
> returns that value.
>
> BB
To me, this looks more like a function call with a forgotten keyword for
a keyword argument.
I think, if -- and this is a big if -- you just want assignment
expressions, the := proposal is best.
Or how about this:
while fd.readline() -> line:
print line
swapping the arguments of the -> operator and using <- instead wouldn't
work, since <- already has a meaning:
x <- 1
is equivalent to
x < -1
=> instead of -> would also work, in case -> gets confused too much with
the C/C++ operator, but it also looks similar to >= .
--
Bernhard Herzog | Sketch, a python based drawing program
her...@online.de | http://www.online.de/home/sketch/
This sort of assignment sucks. Python is intented to be neat,and
clear, it's far preferrable to separate both things.
( S1; S2; (x1;x2; x3; fun((x1;x4;x6;fun(u)); e5);e7)
With powerful structures people tries to "improve" and explain
things with less code ---> the code gets far more obscure.
I do remember my first months with python:
I used only "for" structures because I didn't know "while" existed
in python. Well, the code I wrote using only "for"'s is the
most clear I've ever written. When people have half a dozen of
flow instructions, ...
In fact there're only two: iteration , selection and sequence.
for's, if's and each sentence after another. That's the grandeur
of python, its simplicity.
I admit that visions of such horrors have occured to me. However:
>With powerful structures people tries to "improve" and explain
>things with less code ---> the code gets far more obscure.
This is NOT a valid objection. Why not? Because it can be used against
ANYTHING, with no rebuttal possible. I've heard it used against one of the
nicest things in Python, the ability to string together comparison
operators, like this: 3 < x <= y. Their worry was, "what if someone wrote 3
< x == y > w + 1 <= 7? It would look horrible!"
Now, imagine something like this:
while x=f.readline(); x == "": pass
If you can find an objection to this usage, probably the cleanest and most
directed of the uses being discussed, then you've got a powerful point.
As a matter of fact, I think it's ugly, but not unbearably so.
I do have a problem with the use of semicolons; it's giving them a double
meaning, and like many of the meaningful operators discussed earlier, their
appearance is easily confused with the highly significant : operator.
In other words, I don't have any good objections. They sure make me
nervous, though, and if it were up to me they'd not make it in.
>I do remember my first months with python:
>I used only "for" structures because I didn't know "while" existed
>in python. Well, the code I wrote using only "for"'s is the
>most clear I've ever written. When people have half a dozen of
>flow instructions, ...
...they can't write some programs which they otherwise could. Or more
accurately, they can't visualize some solutions which they otherwise could.
Larry Wall is wrong on a lot of things, but he's right in saying that people
can think only the thoughts that their language allows them to.
>In fact there're only two: iteration , selection and sequence.
Only two? Well, let me add another to make a perfect five. I mean three.
"Nesting." Nicely provided by functions, of course.
>for's, if's and each sentence after another. That's the grandeur
>of python, its simplicity.
Superficially, it's simple. However, look a bit deeper and you see the
problems. for loops require a completely evaluated list in order to
operate, so large or indeterminate operations can't be carried out by them.
Function calls have a HUGE overhead due to flexibility added for certain
special cases.
And so on.
I'm not dissing Python. It's the best language I know of, at least for
beauty. The point is that because of some of the beautiful compromises, the
programmer sometimes has to choose a less beautiful operation to do the
work.
Most commonly obvious is, of course, while loops: they're chosen when for
loops would get too big. Not so obvious is function calls -- all that
happens is that the program slows down, so we don't really care.
--
-William "Billy" Tanksley
"There is nothing more silly than a silly laugh."
-Gaius Valerius Catullus
In article <slrn7bq9d5....@dolphin.openprojects.net>,
wtan...@dolphin.openprojects.net wrote:
>
>Now, imagine something like this:
>
>while x=f.readline(); x == "": pass
>
>If you can find an objection to this usage, probably the cleanest and most
>directed of the uses being discussed, then you've got a powerful point.
>As a matter of fact, I think it's ugly, but not unbearably so.
I don't like this at all; I think it looks ugly and more importantly
I think the ugliness is a reflection of a violation of good software-
engineering practice. There's a saying that data should be used to
capture the regularity in a program and that code should capture the
irregularities; this construct is ugly precisely because it violates
that piece of wisdom. (BTW, I don't recall who said it, and would
be much obliged for an attribution.)
Basically, what we want to do is to mess with streams in the right
way. We begin with the observation that for small files, people are
often advised to write:
for line in file.readlines():
if line == "":
do_stuff()
else:
do_other_stuff(line)
The problem that arises is that the readlines() method reads the
whole file object into memory, which is potentially a highly
resource-hungry operation. (This is why this idiom is advised
only for small files.)
Now, what is needed is some way of doing the same thing, only without
the possibly-huge cost of evaluating the whole stream all at once.
That is, we want lazy evaluation of this stream. Where else in Python
do we see this sort of lazy evaluation?
In xrange(), of course. Rather than writing things like range(1000000),
most people write xrange(1000000) -- avoiding the cost of creating a
list with a million elements.
Likewise, I think most of the complaints would cease immediately if
we could write:
for line in file.lazy_readlines():
if line == "":
do_stuff():
else:
do_other_stuff(line)
just like we can now write:
for i in xrange(1,1000000):
if test_primality(i):
do_things(i)
else:
do_other_things(i)
This is effectively what all those other class wrappers (like Pita)
allow us to do -- they turn a file object into a Scheme-style
lazily-evaluated stream. (And yes, all of the intelligent bits
in this post are lifted straight from Chapter 3 of SICP.)
So I think the Right Thing is to figure out some consistent way of
adding lazy evaluation of streams (just sockets and file objects, I
think), so they can be fed to for loops without insane overhead.
It's not really even a question of semantics or syntax -- we
already have the fileinput module as an existence proof that
it's possible. What's needed is for the built-in types and
classes to be consistently extended to enable this idiom.
Here's an implementation in a dozen lines of Python:
class LazyReader:
def __init__(self, file_descriptor):
self.file_descriptor = file_descriptor
self.line_number = 0
def __getitem__(self, i):
if i != self.line_number:
raise RuntimeError, "accessing lines out of order"
line = self.file_descriptor.readline()
self.line_number = self.line_number + 1
if not line:
raise IndexError, "end of input reached"
return line
It could be used like this:
fd = open("filename.txt", "r")
foo = LazyReader(fd)
for line in foo:
print line
fd.close()
If this class's __getitem__() method were added to the
implementations of the file objects, then we could simply
write something like:
fd = open("filename.txt", "r")
for line in fd:
print line
fd.close()
Though it might be better to farm it out to a method so that
we can write fd.lazy_readlines() for clarity. I would offer
to make the necessary changes myself, except that I program
C about as well as I hold my liquor -- i.e., really poorly.
Neel
Should generalized laziness be available in the language, the idea of
creating lazy interfaces to system objects would be largely obviated.
You could simply tuck your usual read inside a lazy evaluation, as I
envision it.
Thoughts?
--
Alexander Williams (tha...@mindspring.com)
Sometimes you bleed just to know you're alive.
====================================================================
"Robert Smith is a gorgeous man, better than myself in nearly every
way, I think. Chiseled from Florentine marble, smart as a whip,
rich as Croesus, strong as a bear, the ladies love him. Me?
Chiseled from sourdough batter, smart as a rod puppet, strong as a
gopher, rich as a novelty salesman, the ladies whisper amongst
themselves 'Who is that icky guy?' Still, I have ... oh, who am I
kidding? There's nothing to mitigate it."
Haskell is great, isn't it? It and Scheme are the two languages
that are the most Pythonic of all the languages are entirely
unlike Python. :)
>Should generalized laziness be available in the language, the idea of
>creating lazy interfaces to system objects would be largely obviated.
>You could simply tuck your usual read inside a lazy evaluation, as I
>envision it.
Lazy evaluation and mutable state do not mix very well in general.
It leads to highly weird and incomprehensible code, kind of for
the same reasons that writing multithreaded code is tricky.
Haskell solves this through the simple expedient of not having
any mutable state, but Python will never be a pure functional
language.
So IMO lazy operations should be added conservatively, only when
they are *both* the natural and the cleanest way of representing
the solution to a problem. I think that it's worth adding in the
case of file objects, but I'm not sure about any others.
Note that this whole thing is only worth arguing about because
we can't subclass from C types -- then it would have been trivial
to add the file.lazy_readlines() method. So to add it there needs
to be changes to the C code and people are rightly more worried of
making mistakes there.
(Was it Paul Prescod who asked for the appropriately general solution
to this problem? I think "we could eliminate the need for assignment
in conditionals if we could subclass from C types" is probably the
biggest suggested so far....)
Neel
>Haskell is great, isn't it? It and Scheme are the two languages
>that are the most Pythonic of all the languages are entirely
>unlike Python. :)
Between the three of them, I think they really summarize all the best
ways to approach algorithmic problem-solving in general. Python
covers OOP/iterative, beautifully, Scheme with closures/lexical scope,
and Haskell with pure functionality and some of the odder corners of
fully lazy evaluation. I think it definitely bodes well to have a
fully stocked toolbox of concepts and tools.
>Lazy evaluation and mutable state do not mix very well in general.
True enough; the structures for support of laziness in Scheme are even
still a bit unwieldy in my eyes. But useful, their use is really
something when the right algorithm comes along. And occasionally
it'll be useful enough even for the wrong algorithm.
>Haskell solves this through the simple expedient of not having
>any mutable state, but Python will never be a pure functional
>language.
Well, if you don't mind using CPS-style, you can get a fair simple
rendering of mutable state going on. Likewise if you master Monads
(and I am a /long/ way from mastering Monads). Python *could* learn a
lot from the Haskell/Scheme community about being friendlier to
recursive solutions; as it stands, I try to avoid recursion in Python
even when it seems the logical choice of algorithmic structure because
its such a bloody pain to do neatly and its wasteful as all hell.
>to this problem? I think "we could eliminate the need for assignment
>in conditionals if we could subclass from C types" is probably the
>biggest suggested so far....)
Indeed. Overall, and in the end, this general solution will solve
much more than this single-point problem.
I'm in moderate disagreement, but only because I think it looks ugly. I
have to admit that there are reasons to like it -- for one thing, it makes
one of the Tough Issues more approachable in a general sense: how to place a
statement in an expression context.
Personally, I would rather do a rework of the grammar.
>wtan...@dolphin.openprojects.net wrote:
>>Now, imagine something like this:
>>while x=f.readline(); x == "": pass
>>If you can find an objection to this usage, probably the cleanest and most
>>directed of the uses being discussed, then you've got a powerful point.
>>As a matter of fact, I think it's ugly, but not unbearably so.
>I don't like this at all; I think it looks ugly and more importantly
>I think the ugliness is a reflection of a violation of good software-
>engineering practice. There's a saying that data should be used to
>capture the regularity in a program and that code should capture the
>irregularities; this construct is ugly precisely because it violates
>that piece of wisdom. (BTW, I don't recall who said it, and would
>be much obliged for an attribution.)
Two objections have I to your objection.
1. I hear you saying that, but I don't hear you justifying it. Why does it
violate that rule?
2. I suspect that you've reversed the rule -- data is protean and can take
any shape; code is inflexible and even Scheme can't change it once it's laid
down.
>Basically, what we want to do is to mess with streams in the right
>way. We begin with the observation that for small files, people are
>often advised to write:
>for line in file.readlines():
[but he wants...]
>for line in file.lazy_readlines():
That would be sufficient for me, although it's merely the simmplest case of
what some people want. Actually, though, I wouldn't mind it if readlines()
were always lazy. Perhaps a new type of sequence, the lazy list?
>for i in xrange(1,1000000):
Yup.
But really, although this would be nice and I'm sure it'll see a lot of use,
there are more sinister forces backing this proposal.
>Neel
I think that was me, in response to Paul Prescod. :)
By the way, doesn't there already exist a file reading class that does
lazy evaluation? I'm starting to feel more and more for a solution
involving a clear use of lazy evaluation; and subclassing from Python
types would surely help. Especially since foo; bar; baz aka
'supertuples' are starting to sound scarier all the time.
Regards,
Martijn
It doesn't seem to be what ppl want, but fileinput.input creates an
instance of the fileinput.FileInput class.
# a little merge program
import fileinput
a = fileinput.FileInput('file1')
b = fileinput.FileInput('file2')
i = 0
while 1:
try: line1 = a[i]
except IndexError: line1 = ''
try: line2 = b[i]
except IndexError: line2 = ''
if line1 and line2:
sys.stdout.write(line1)
sys.stdout.write(line2)
elif line1:
sys.stdout.write(line2)
elif line2:
sys.stdout.write(line1)
else:
break
i = i + 1 # note that __getitem__ checks the value of "i"
a.close()
b.close()
It was suggested _many_ times (by myself once) that the FileInput class
be used for this one idiom.. but ppl wanted to corrupt one of Python's
early philosophies: no assignment in expressions. Python has been
described as a "language very much like pseudocode" to praise its
clear, clean syntax and idioms. But then some people think C and Perl
have clear, clean syntax.
-Arcege
> It was suggested _many_ times (by myself once) that the FileInput class
> be used for this one idiom.. but ppl wanted to corrupt one of Python's
> early philosophies: no assignment in expressions. Python has been
> described as a "language very much like pseudocode" to praise its
> clear, clean syntax and idioms. But then some people think C and Perl
> have clear, clean syntax.
I don't think people suggested ways of doing this so that we could get
closer to C or Perl. The problem was that some things aren't easily
done with the current control structures in Python... Like the double
assignment in a while loop:
x = some_complex_expression
while x:
do_something()
x = some_complex_expression # Needlessly replicated
The standard solution is:
while 1:
x = some_complex_expression
if not x: break
do_something()
I still think this seems like an abuse of the while loop.
And then there was the example of the mysterious match object...
x = m.match(something)
if x then:
process(x)
else:
x = m.match(something_else)
if x then:
process2(x)
else:
x = m.match(foobar)
if ....
Not exactly nice. Both of these could be solved with either more
powerful control structures or with assignment expressions. Which is
prettier/clearer/more like pseudocode is, I guess, what has been
discussed in this thread. And I *don't* think the solutions above are
the best ones -- from what I've seen, it seems several others agree.
>
> -Arcege
>>>>> pi...@cs.uu.nl (PvO) writes:
>>>>> wtan...@dolphin.openprojects.net (William Tanksley) (WT) writes:
WT> That would be sufficient for me, although it's merely the simmplest
WT> case of what some people want. Actually, though, I wouldn't mind it if
WT> readlines() were always lazy. Perhaps a new type of sequence, the lazy
WT> list?
PvO> Is there any reason why readlines() can't be made lazy? I don't think it
PvO> would break anything. And the performance penalty for lines=f.readlines()
PvO> wouldn't be too big, would it?
After a few second thought I realised that it wouldn't help, as the
implementation would have to keep all the lines in memory just in case
someone would at the last moment try to get an element somewhere in the
beginning.
So to really do this you need a special type that is less powerful that a
sequence, like the Java Streams. [1] So the for would need a Stream after the
in and a sequence would be a specialisation of a Stream. The stream should
support a "get_next" operation, and by default for a sequence it would be
the index operation (get_item). To make that easier the get_next would be
called with an indexed which wouldn't be used by the Stream. Otherwise the
sequences would have to keep state. xrange(..) would be another kind of
Stream. It might even be possible to convert Streams automatically to
sequences, by calling get_next repeatedly. If so, then range and xrange
could actually be the same.
---
[1]See the original proposal by Neelakantan Krishnaswami about
lazy_readlines().
--
Piet van Oostrum <pi...@cs.uu.nl>
URL: http://www.cs.uu.nl/~piet [PGP]
Private email: Piet.van...@gironet.nl
Consensus? Neel, if c.l.py were capable of reaching consensus, we wouldn't
bow to Guido as to a Living God <wink>.
>In article <slrn7bq9d5....@dolphin.openprojects.net>,
>>wtan...@dolphin.openprojects.net wrote:
>>
>>Now, imagine something like this:
>>
>>while x=f.readline(); x == "": pass
>>
>>If you can find an objection to this usage, probably the cleanest and
>>most directed of the uses being discussed, then you've got a powerful
>> point. As a matter of fact, I think it's ugly, but not unbearably so.
> I don't like this at all; I think it looks ugly and more importantly
> I think the ugliness is a reflection of a violation of good software-
> engineering practice.
I guess I haven't said so before, but I don't like it either. It reads
better with parentheses (which I believe were required in Guido's vision):
while (x = f.readline; x == ""): pass
Still, it allows more than is required but less than some desire (contrary
to some non-Guido claims, it does not permit arbitrary stmts to appear
embedded in expressions).
Can't say I really like ":=" or new keywords or the proposals from Mars
<wink> any better, though.
> There's a saying that data should be used to capture the regularity in
> a program and that code should capture the irregularities; this construct
> is ugly precisely because it violates that piece of wisdom. (BTW, I don't
> recall who said it, and would be much obliged for an attribution.)
Beats me, but it seems backwards: some of the prettiest programs on earth
are table-driven, using simple and relentlessly uniform code to untangle
masses of complications buried in data tables. Hiding irregularities in
tables makes them much easier-- and safer --to change.
> Basically, what we want to do is to mess with streams in the right
> way.
The toughest problems here are untangling deeply nested "if" nests, not
loops.
[recap of reading lines from files, and lazy evaluation]
> ...
> So I think the Right Thing is to figure out some consistent way of
> adding lazy evaluation of streams (just sockets and file objects, I
> think), so they can be fed to for loops without insane overhead.
May be nice for its own sake, but doesn't help the "if" messes.
> It's not really even a question of semantics or syntax -- we
> already have the fileinput module as an existence proof that
> it's possible. What's needed is for the built-in types and
> classes to be consistently extended to enable this idiom.
OK, since fileinput already handles files, we add socketinput and we're done
<wink>.
> ...
> Though it might be better to farm it out to a method so that
> we can write fd.lazy_readlines() for clarity. I would offer
> to make the necessary changes myself, except that I program
> C about as well as I hold my liquor -- i.e., really poorly.
In Python2 you should be able to derive from, and even replace methods of,
builtin types (== builtin classes, same thing then). Coding stuff in C
shouldn't be needed then.
OTOH, you should get really drunk and write up the ideal interface you'd
like to see. Then we can get Guido really drunk and trick him into saying
he'll support it.
alcohol-is-the-fuel-of-progress-ly y'rs - tim
I see you have detected my clever plan -- by staking a position on the
other side, I have neutralized the consensus, and now Guido will step
in and reveal to us the Right Thing. :)
>> There's a saying that data should be used to capture the regularity in
>> a program and that code should capture the irregularities; this construct
>> is ugly precisely because it violates that piece of wisdom. (BTW, I don't
>> recall who said it, and would be much obliged for an attribution.)
>
>Beats me, but it seems backwards: some of the prettiest programs on earth
>are table-driven, using simple and relentlessly uniform code to untangle
>masses of complications buried in data tables. Hiding irregularities in
>tables makes them much easier-- and safer --to change.
I think I read it in one of Jon Bentley's books (Programming Pearls,
maybe?) -- but I could very well have typed it backwards; your example
of the smart data structure and the stupid code is exactly the sort of
thing I like.
It makes more sense to me to have a lazy stream that knows what
comes next than to mess around with complicated assignments in
expressions. But you write:
>OK, since fileinput already handles files, we add socketinput and we're
>done <wink>.
I'd almost agree with this, actually. Except that judging from this
thread I have this suspicion that I am one of the three people in the
universe who uses the fileinput module regularly. :)
>> Basically, what we want to do is to mess with streams in the right
>> way.
>
>The toughest problems here are untangling deeply nested "if" nests, not
>loops.
The only example I can recall is the nested regexps, and that seemed
to be an example which could be easily simplified by storing the
regexps and processing functions in dictionaries. This, which
already exists:
for line in fileinput.input(filename):
for key in patterns.keys():
m = patterns[key].match(line):
if m:
processors[key](m)
break
matches pretty closely to the assignment-expression version:
while (line=file.readline; line):
if (x=m1.match(line); x):
process(x)
elif (x=m2.match(line); x):
process2(x)
[...]
Both of these are certainly cleaner than
for line in file.readlines():
x = m1.match(line)
if x then:
process(x)
else:
x = m2.match(line)
if x then:
process2(x)
else:
x = m3.match(line)
if ....
but I would argue that the first is the cleanest of the three
solutions, even aside from the fact that it requires no new
syntax.
Everything in Python is a first-class object; it seems like a
shame not to take advantage of that fact. I'm sure it's possible
to come up with examples where this approach won't fly, but at
some point you have to decide whether the convenience of a special
form is worth the cost in clarity. (I suppose this last sentence
is strictly only true if the "you" in question is Guido, but I
think I'm allowed a rhetorical flourish or two.)
>[recap of reading lines from files, and lazy evaluation]
>> ...
>> So I think the Right Thing is to figure out some consistent way of
>> adding lazy evaluation of streams (just sockets and file objects, I
>> think), so they can be fed to for loops without insane overhead.
>
>May be nice for its own sake, but doesn't help the "if" messes.
It seems like it would be a small enough addition that it could make
it into 1.6; then we could see whether the cries for assignment
in expressions die down or not.
>> It's not really even a question of semantics or syntax -- we
>> already have the fileinput module as an existence proof that
>> it's possible. What's needed is for the built-in types and
>> classes to be consistently extended to enable this idiom.
>
>> ...
>> Though it might be better to farm it out to a method so that
>> we can write fd.lazy_readlines() for clarity. I would offer
>> to make the necessary changes myself, except that I program
>> C about as well as I hold my liquor -- i.e., really poorly.
>
>In Python2 you should be able to derive from, and even replace methods of,
>builtin types (== builtin classes, same thing then). Coding stuff in C
>shouldn't be needed then.
Yes, but I'm curious to see what fraction of the pleas for assignments
in expressions are the result of file objects. The case for or against
is different if it is 95% than if it is 5%.
One of the reasons I'm so against assignment expressions is that I
learned an awful lot of Python by reading the libraries. The code is
so clean that's it's there was no fear level to overcome before trying
to read it -- I didn't worry that I'd have to learn head-breaking amounts
of special tricks before I could read the code. I think that seeing
characters like (;) in unfamiliar contexts really would have discouraged
me from trying to understand what was going on.
>OTOH, you should get really drunk and write up the ideal interface you'd
>like to see. Then we can get Guido really drunk and trick him into saying
>he'll support it.
The best-specified interface is code, right? A patch seems like it
would be a lot more convincing than any amount of bloviation about
lazy evaluation and the beauty of a well-structured program.
Damn. I'm almost tempted to risk tearing my hair out in frustration
over pointer problems to try it out, even though I /know/ that
programming in C is like trying to juggle live grenades (i.e.,
possible, but not really a good idea when unnecessary). It just
seems like it couldn't be that hard -- I can just crib from the
xrange code and the readline() method, right? (Famous last words,
I know.)
Neel
x = foo()
while x:
do_something()
x = foo()
You have some problem with this?
--
--- Aahz (@netcom.com)
Hugs and backrubs -- I break Rule 6 <*> -=> http://www.rahul.net/aahz
Androgynous poly kinky vanilla queer het
I guess I mostly see Life [tm] as a process of closing doors that you
might want to go through.
> x = foo()
> while x:
> do_something()
> x = foo()
> You have some problem with this?
*I* have a problem with this ... ;-)
I often need to enter such a while construct at least once and have
to add two lines setting "x" like above ...
What *i* would like to see is adding a "do" construct, allowing for this:
do x:
do_something()
x = foo()
I that "do" would check "x" only at the end ...
OKOK - "x" does not exist at this point, so we would need it the other way
around (probably too ugly):
do_something()
x = foo()
do x:
Anyway - don't worry about this ;-)
Byebye,
--
Thomas Ackermann | Tel. +49-(0)228/631369|73-7773 | <t...@math.uni-bonn.de>
finger t...@rhein.math.uni-bonn.de for public key
GNU LINUX Python gtk pygtk MySQL FUDGE GURPS
That's a completely separate issue from the assignment as expression
problem we're talking about. You're asking for a new structure, either
"do...while" or "repeat...until" (I prefer the former to avoid inverting
the conditional).
[...]
> > x = some_complex_expression
> > while x:
> > do_something()
> > x = some_complex_expression # Needlessly replicated
>
> x = foo()
> while x:
> do_something()
> x = foo()
>
> You have some problem with this?
So -- what you have basically done is repeat what I wrote and ask me
if I have a problem with it? As I said -- yes, I do. I do notice that
you have made a function out of it, and that was one of my suggestions
earlier on to. (Alto, makine a lambda or something.) Actually, I think
even the Pita suggestion is prettier than that.
The problem, I think, is that this is a symptom of the lack of a
control structure. But it't not *that* problematic -- I still think
it's generally prettier than the while 1:/break solution (which really
seems like a hack.)
[...]
>
> What *i* would like to see is adding a "do" construct, allowing for this:
>
> do x:
> do_something()
> x = foo()
>
How would this help?
do line:
process(line)
line = readline()
The loop would have to start at the end for this to work.
> do_something()
> x = foo()
> do x:
This wouldn't work any more than the above.
Anyway -- it can easily be implemented like this (though this is of
course a hack too...)
first = 1
while first or line:
if first: first = 0
line = readline()
process(line)
This has the same problem, though, in the last iteration. So...
Another version (just being carried away, here...)
first = 1
while first() or line:
first = 0
line = readline()
if line:
process(line)
At least we don't have a redundant assignment -- but we *do* have a
redundant boolean variable; another standard symptom of a lacking
structure...
> In article <dftq97...@final.math.uni-bonn.de>,
> Thomas Ackermann <t...@math.uni-bonn.de> wrote:
> >
> >I often need to enter such a while construct at least once and have
> >to add two lines setting "x" like above ...
>
> That's a completely separate issue from the assignment as expression
> problem we're talking about. You're asking for a new structure, either
> "do...while" or "repeat...until" (I prefer the former to avoid inverting
> the conditional).
That's been a majour part of this thread since the beginning. I
suggest you read up on it in dejanews.
Turn that last line into "and while y: pass" and you've got a winner.
Otherwise you're starting a brand new loop. Having an empty "while" mean
"while 1" seems fine to me, but I wouldn't sweat to make it happen; The "1"
just isn't that nasty. YMMV.
This is the fundamental dilemma of those who really want a "do...until": the
closing suite-less line it cries out for is found nowhere else in Python,
and would need to be syntactically distinct from the starting and
intermediate clauses (and don't forget the optional "else"!). Avoiding the
forbidden novel keyword means you'd be saddled with something like this:
while 1:
# sing a manly song
if lumberjack_is_a_pooftah(): break
elif voice_gone(): # by analogy with "else", natch
gargle()
I could go on with further evil constructions like "else while" to segue
into a following loop, but I learned my lesson with "and if" and don't want
to sour anyone on "and while" <wink>.
Mmmm... the larch
Evan Simpson
I'm shooting from the hip here but... how about a syntax like:
while :
do_something()
x = foo()
while x:
This might (???) make those who hate to write
while 1 :
happy, and have the added benefit of allowing things like:
while :
do_something()
x = foo()
and while x:
do_something_else()
y = bar{}
while y:
If my recollection is correct, I believe Guido smiled on the "and while"
construct.
Voila! The universal solvent!
Heading for my bomb shelter,
George
do:
line = getit()
doit(line)
while line
(note the missing ":" after while)
This was my last idea ;-)
> What about this:
>
>
> do:
> line = getit()
> doit(line)
> while line
>
> (note the missing ":" after while)
>
Well -- it's not quite new... And it doesn't solve the problem... What
if doit doesn't know how to tackle an empty line? Then it would
crash... (And the block-ending thing is very *un*-pythonesque...)
>
> This was my last idea ;-)
>
--
1. It would allow addition of a corresponding magic method for classes.
For now, I suggest '__assign__'. ('__equals__' is ambiguous.)
This would eliminate what some see as a hole in the current repitoire.
Example:
class point:
def __init__(self,pair):
try: self.x1,self.x2 = pair
except: #whatever
def __assign__(self, pair): point.__init__(self, pair)
# in general, update might require something other than re-init
...
x = point(1,2)
...
x = (3,4)
This would make it possible to update an instance with familiar syntax
and without either defining/using an explicit set method or exposing the
internals of the class.
2. It would make interactive use of the interpreter a bit easier.
In batch mode, a naked assignment expression would have the same effect
as an assignment statement. So newcomers might ask, "Why does Python
have both an assignment statement with '=' and an assignment expression
with ':='?" The defensive answers are 'historical accident' and '=/==
confusion/bug avoidance'. The positive reason is to make Python easier
to use.
Aside from the points alread raised, there is the difference between
statements and expressions in interactive mode. Like most other
statements, assignment statements do generate any not output. In
particular, they do not display the value assigned. While quite
sensible for assignment of literals, one often wants to see the result
of function calls and other calculations. At present, this requires
re-typing the left-hand side of the statement. An assignment expression
would/should display the values, just as all other expressions do, at
the small cost on typing one extra character.
For instance, working interactively, one might want
>>> line := file.readline()
to see the line read and assigned, but would be glad that
>>> lines = file.readlines()
is available to quietly and quickly read a 1000-line file.
In other words, a historical accident could be justified, retroactively,
as a smart design decision :-)
Terry J. Reedy