re: syntax feedback from LANL course (very long)

I taught Python at Los Alamos National Laboratory for the second time two weeks ago, and finally found time last night to go through both sets of feedback forms. Given the current discussion on python-dev of ternary conditionals, namespaces, etc., I figured syntax issues would be most topical. Most of the students were in their 20's or 30's, with a handful in their 40's and 50's. When asked what they used on a daily basis, they split pretty evenly between Fortran and C++ (no C-but-not-C++ responses). They also divided pretty evenly between "numerical and/or visualization" and "systems programming". (At the lab, the latter means everything from making the web archive of past experimental results searchable, through to porting HDF-5 to massively-parallel machines and hacking the Linux kernel to make inter-process communication run faster.) 28 students had Ph.D.'s (all in math, physical science, or engineering); nine had M.Sc.'s (one in C.S.), and the three B.Sc.'s were all in C.S. So here goes... ------------------------------------------------------------------------- 0. "Hey, using indentation to show nesting is cool!" 'Nuff said --- thanks, Guido. ------------------------------------------------------------------------- 1. "What's with 'while 1: break' for normal loops?" This was the most common complaint about syntax --- neither Fortran nor C programmers found it natural to create an infinite loop, then jump out of it. (Several of the C++ programmers said that house style in their groups only allows 'break' for truly exceptional cases.) One student suggested the following loop-and-a-half notation: do: first-half while condition: second-half which has the nice property that both of the degenerate forms: do: first-half while condition # no trailing ':' and while condition: second-half are useful in their own right. Tim Peters tells me that this has been discussed many times before, and that the main obstacle is the introduction of a new keyword. He mentioned: while: first-half and while condition-1: second-half and while condition-2: third-half # [sic] and while final-condition # no trailing ':' as an interesting (but possibly confusing) generalization. ------------------------------------------------------------------------- 2. "Using range() in for loops is clumsy." My audience does a lot of 'for' loops over numerical bounds, and would prefer something slice-ish like: for i in 0:10:2 : body I think they could live with: for i in [0:10:2] : body I believe Brian Harvey mentions somewhere in "Computer Science Logo Style" (the best programming books for kids I've ever seen) that it was important to make basic counting loops very, very simple to write. It's a different audience (10-year-olds instead of Ph.D.'s with 30 years of Fortran behind them), but maybe worth examining. (While we're on the topic: is there an easy way to construct a slice like 3:10, pass it around as a variable, and later use it to index an arbitrary sequence?) ------------------------------------------------------------------------- 3. "Where is '+=' ?" and "Why can't I redefine assignment?" To my surprise, no-one asked why assignment wasn't an operator. However, a lot of people wanted in-place modification --- it's one of the innovations of C and derived languages that scientific programmers appreciate, since it saves them having to write 'pressure_gradient[(ix+1)*3+idx][iy-1-(idx%2)]' on both sides of an assignment, and then remember to update both occurrences when they notice the typo. (The '%' should be a '/'.) Several of the students who ticked the box "Familiar with object-oriented programming" asked why they could redefine addition, membership, etc., but not straight assignment. Guido explained the reasoning to me at IPC8, and pointed out that classes can always define a '.set(...)' method. However, I'm pretty sure that 'set()' will be a non-starter with this crowd (I can explain why if people care). ------------------------------------------------------------------------- 3.1 Min and Max On a related note, I also had requests for redefining "min" and "max" (e.g. to handle objects representing bounding volumes in 3D graphics). As a certain kind of mathematician will point out, these are really binary operators, just like "+", and it's only an historical accident that they aren't treated with the same respect (I'm quoting here). I mentioned this to Guido, and suggested the C* notation: a = x <? y # min b = x >? y # max He said (rightly) that '<?' and '>?' won't be familiar even to experienced programmers, but that user-definable 'min' and 'max' might be do-able. The only issue then is that if '+=' makes it into the language, something like 'min=' ought to as well... ------------------------------------------------------------------------- 3.2 Case Statement, and Remembering Matches in Conditionals Another related point came up in discussion of regular expressions. The code I showed them was: pat1 = re.compile(...) pat2 = re.compile(...) ...etc... while 1: line = ...get input... m = pat1.match(...) if m: code continue m = pat2.match(...) if m: code continue ...etc... Several students said that it wasn't at all clear that the various matches were intended to be mutually exclusive; one student became very frustrated trying to debug this code after accidentally deleting a 'continue'. If there's a cleaner way to structure this, I'd be grateful for an example. One student (a physicist who now does computer graphics) sent me: if x is: expr1, expr2: code using x (which is either val1 or val2) expr3: code using x (which is guaranteed to be val3) else: code using x (which is something else) which would make the above: while 1: line = ...get input... if m is: pat1.match(...): code using m pat2.match(...): code using m etc. (I'm not advocating, I'm just reporting...) ------------------------------------------------------------------------- 4. "Aren't tuples redundant?" I explained that the const-ness of tuples was needed so that they could be used as dictionary keys. The guy with Perl in his background immediately asked if that's reliable --- a tuple can contain a list, which can be mutated. You've all heard this one more times than I have... ------------------------------------------------------------------------- 5. "Why can't I put an 'except' after an 'if'?" This one surprised me. Several students wanted to be able to say: if (dict['fred'] > 0): code except KeyError: other-code or: for i in seq: i = i ** 2 except ArithmeticError: error-handling-code or even (from one guy with Perl in his background): dict['fred'] = dict['fred'] + 1 except KeyError: dict['fred'] = 1 They pointed out that 'try' does nothing except introduce a block, so why not just use the blocks that other keywords introduce? I'd be interested in knowing whether other people's students think like this... :-) ------------------------------------------------------------------------- 6. "There is no number six." That's really all that came up about Python syntax. None of the students were OO expects, so meta-classes and the like weren't raised. Generic programming did come up once, but the guy who asked hadn't clued in that typing is dynamic. The same guy also asked why he couldn't interleave anonymous and named function arguments, as in "def foo(a, b=3, c, d=5)" also came up, but everybody else who expressed an opinion said they'd find that confusing. Hope it's useful, Greg

Sort of. Slice objects can be created with the slice builtin. slice(1,10,2) is meant to be equivalent to 1:10:2, but only NumPy knows how to deal with slice objects in the getitem slot (use None to express 'omission' of an index). It's on the TODO list for 1.7 at this point, although if someone submits a working patch to 1.6, who knows... JPython does it right, I believe. --david

[Greg Wilson]
I guess you didn't try it:
To pass the hash test, it needs to have immutable contents as well. To my mind, the "can act as dictionary key" argument is a red herring. Tuples and lists may technically be almost the same thing, but I find I hardly ever need to ponder whether I should use a tuple or a list. Tuples are structures and lists are containers. Yes, you can use a container as a structure. You can also use a bucket as a spoon. or-stick-your-head-straight-into-the-trough-ly y'rs - Gordon

GmCm
[Greg Wilson]
4. "Aren't tuples redundant?"
They're not structures the way a C programmer thinks of a struct or a pascal programmer thinks of a record. Do you have a better way of justifying their existence to novices who haven't reached the zen? It's a nontrivial pedagogical problem in my experience as well. (yes, this is a better topic for edu-sig than for python-dev). --david

That doesn't matter; I'd like to assume that we want to teach students who don't already have a preconceived notion of a Pascal record or C struct.
I have a feeling that introducing optional static typing would help here, because it clarifies the concept. For example, these two have the same type, [int]: (the length is part of the value) [3, 100] [5, 6, 7] On the other hand, these two have different types: (3, 100) # (int, int) (5, 6, 7) # (int, int, int) If we start mixing types, we may get [3, "wow"] # [any]; or perhaps [int|str] (3, "wow") # (int, str) We can then explain that we might have informal names for the parts, and we can use these as local variable names. This is borrowed from ABC, which did at least *some* user testing on issues like this: count, value = t t = count, value (ABC used a different assignment syntax; it would be: PUT t IN count, value PUT count, value IN t I am still friends with the librarian who was taught ABC as a guinea pig.) However, ABC was a statically typed language in the sense of Haskell: there were no declarations but the types were inferenced from initializations and operators. Maybe that *does* make a difference. Also, its tuples were completely unlike sequences: they didn't have indexing or slicing. There were no tuples of lengths 1 or 0. --Guido van Rossum (home page: http://www.python.org/~guido/)

For CP4E, absolutely. I was just reporting on my experience with existing programmers not unlike Greg's (also with many fewer PhD's)[*].
I have a feeling that introducing optional static typing would help here, because it clarifies the concept.
Very interesting point -- however, I think that we need a better answer in general. The question in my experience stems from a desire to minimize memory load. The students grok lists in about 30 seconds. Then I explain tuples, and they wonder why they were "added" given that lists "exist" (in their brain at that time =). --david [*]: I did teach a course at Bank of America where 15 of the 16 people in the room were Vice Presidents. Of course, they were just programmers, but the pay for a competent programmer is high in the hierarchy of bank salaries. =)

[Guido]
Very much the same applies to functional languages, except (usually) even more so. For example, Haskell has both lists and tuples, but lists are homogenous (and the type system has no union types, at least not of the int|str form -- the list [3, "wow"] can't be expressed (although the tuple form can be): ? [3, "wow"] ERROR: [Char] is not an instance of class "Num" ? (a Haskell string is a list of Char, which is what [Char] means)). At heart, tuples are for Cartesian products of a fixed number of possibly differing types, while lists are for arbitrary-length sequences of a single type. This is good style even in Python! (Think about it: when you see a newbie post on c.l.py that violates your Python intuition wrt list or tuple use, in my experience it almost always violates one of those (hitherto never expressed <wink>) guidelines.) That lists are allowed to be heterogenous should really be considered an advanced feature; ditto list operations (like indexing) on tuples. BTW, despite the recent unpleasantness with the TeachScheme! folks, there's a lot to be learned from DrScheme, part of which is the notion of language "levels": the IDE can be set to restrict the newbie to various (nested) subsets of the full language, so that new learners are shielded from the hairier stuff until their confidence builds. metaclasses-are-a-good-candidate<wink>-ly y'rs - tim

I guess you didn't try it:
I actually had -- the point I was trying to make was that his first reaction was "But what about..." Maybe it's just a reflection of his Perl background :-). Like I said, you've all heard this one more times than I have... Greg

On Thu, 3 Feb 2000 gvwilson@nevex.com wrote:
Wow, these were really informative. Thanks for collecting the results.
1. "What's with 'while 1: break' for normal loops?"
I'd probably agree that this is a significant thing. I think it would be very nice to have do...while, and of all your examples i liked do: first-body while condition: second-body best. This very nicely takes care of the common idiom do: line = file.readline() while line: body (Once upon a time i even wrote a patch just to handle this case with a strange "while line from file.readline()" syntax. I think the above is much nicer.)
This *is* confusing. Does the "third-half" get executed if condition-2 is true, or only if condition-1 and condition-2 are both true? Hmmm... i suppose if i interpret each "and while <cond>" as "if not <cond>: break" it makes more sense. But i still like the first. "do" doesn't even have to be made into a keyword for the do...while construct to work: there is no other valid construct in which a name would appear followed by a colon as the first thing in a statement or expression, so the parser should be able to figure it out.
Wow, i like that. There is a nice symmetry to >>> t = (3, 7, 5, 6, 2, 1, 8) >>> print t[0:5] (3, 7, 5, 6, 2) >>> for i in [0:5]: print t[i] ... 3 7 5 6 2
I'd tend to agree with that. This is definitely one of the first things any beginner will see, and it's nice not to be sidetracked into an explanation of "range". The symmetry means that, after they've seen "for i in [0:10]:", they'll already have a feel for what "list[0:10]" ought to do. Oh, and having a slice object work in a "for" would allow us to drop that vaguely irritating range/xrange distinction. That said, however, i don't think i'd make a really big deal out of this. Maybe i'm being too swayed by the cuteness of the idea.
I was wondering why this doesn't work. Given that slices are built in, the lack of discussion of them in the documentation makes them rather mysterious. It would be nice to make them just do the expected thing. You could then do things like putting list[::-1] in an expression without having to copy it to a temporary, call reverse(), and then use the temporary.
-------------------------------------------------------------------------
3. "Where is '+=' ?" and "Why can't I redefine assignment?"
+= would be nice; not a really big deal to me. Redefining assignment would get way too confusing, i think. __setitem__ has always been good enough for me.
-------------------------------------------------------------------------
3.1 Min and Max
Interesting, but doesn't matter much to me.
-------------------------------------------------------------------------
3.2 Case Statement, and Remembering Matches in Conditionals
[...]
I like this quite a lot! One question: in that last "else" clause, wouldn't "x" be undefined?
-------------------------------------------------------------------------
5. "Why can't I put an 'except' after an 'if'?"
Possibly interesting, but it doesn't seem necessary to me. Having the "try:" at the beginning of the block more clearly delimits what's being protected. Wouldn't this look weird? if foo.bar > 0: blah else: blah except AttributeError: blah else: blah Now does the "except" cover just the first "else" or both the bodies of the "if" and the "else"? Alternatively, imagine that there used to be only an "if" and we wanted to insert an "else". It's clear we can't get rid of "try:" altogether, so it seems like more work to remember the specific list of other situations in which "except:" might apply...
-------------------------------------------------------------------------
6. "There is no number six."
And six is RIGHT OUT. min-sv�vare-�r-full-med-�lar-ly y'rs, -- ?!ng

The loop breaks out the first time it finds a false condition. I find it confusing as well, and wonder whether the extra generality would ever be used. I'd even want to put: do: first-half while cond: second-half in front of both novices and experts --- is this two loops, or one?
Might not be true in future, so probably dangerous to assume now. (Icon, anyone? :-) Thanks, Greg

Greg Wilson wrote: 3. "Where is '+=' ?" and "Why can't I redefine assignment?"
It'll make a big difference to NumPy (avoiding temporaries, etc.). Also hard to explain why you can redefine assignment to an array element, but not to a scalar variable. Thanks, Greg

FWIW, I never got that question by my students. I did get that question in a consulting context, but it was a very advanced problem having to do with needing control over references to huge external objects, etc. After much trial and error I've come up with a 'method' for describing assignment, references, names, etc., which I've found works well in practice. I demonstrate the concept of names & references on a whiteboard with trivial cases like a = 1 + 3 and then I move on to b = 3 c = 5 a = b + c and then a = [b,c] and then I graduate to the a = [0]*3 and then finally a = [[0]*3]*3 and when they get that I know I've succeeded in getting the concept of assignment in Python across. It's much too hard to describe in email because it requires a blackboard and drawing arrows, erasing arrows, etc., but someday I can demonstrate (SD2000?). Anyway, it's possible that your students are just too smart for their own good, Greg. =) --david

I wish I could tell from my notes whether the ones asking to redefine assignment had C++ in their background or not. Setting the consistency issue aside, libraries like NumPy (and the Python futures package I keep meaning to write) will be a lot cleaner from a user's point of view if assignment is redefinable. However, I do understand that there's no easy way to do it without taking an unacceptable performance hit... I'll send anyone who can come up with a clean, workable solution a case of Canadian beer (now how's *that* for a real prize? :-) Greg

(Quick flip through notes): "x is None in the else branch". Looking at it again, it came up as part of the question "Why isn't assignment an operator?" The student in question was used to doing: if (x = foo()) { body } else if (x = bar()) { body } else { body } and wanted to have something in Python that would (a) provide more flexibility than a C/C++ case statement, while (b) making it clear that the alternatives really were mutually exclusive. I think. Or maybe not. He talked really, really fast... Greg

Just picking on the loop issue here. [Ka-Ping Yee, on Greg Wilson's post]
Wow, these were really informative. Thanks for collecting the results.
Ditto! It's posts like these that make me glad Guido has so much spare time <wink>.
1. "What's with 'while 1: break' for normal loops?"
I'd probably agree that this is a significant thing.
Me too: it's the ugliest thing in Python newbies hit right away, and while *I* can't really "see" the convolution in "while 1: ... if xxx: break ..." anymore, that's learned behavior; but I get reminded of how grating it is at first each time someone new at work starts learning Python. This one made Andrew's "Python Warts" paper. Enough is enough already <0.5 wink>.
Actually suggested by Guido Himself on Christmas Eve of '98 (see DejaNews; I found the URL for Greg offline but have since lost it). And the IDLE editor already handles it fine <wink -- but that means pymode is likely to too, and so also "most things of that kind">.
This *is* confusing.
It's from an old c.l.py thread; don't recall who suggested it; I don't like it myself; "and while xxx:" is just sugar for "if not xxx: break"; its advantage is the absence of new keywords; the "while:" at the top reads poorly; the semantics of "and while" are indeed unclear at first (IIRC, Christian took way too many drugs at the time <wink> and tried to generalize it to "and if" as well ...).
Good point! Not the kind of point Guido warms to instantly, but a good point all the same. I had already checked, and there are at least two functions named "do" in Python's own std distribution. So making "do" a keyword is very unattractive. I'm not sure "repeat:" (a la Pascal) or "loop:" (Icon? too lazy to look it up -- *one* of those loser languages I'm always irritating people about <wink>) would actually be better. If hackery could overload "do" (or repeat, or loop), I'm in favor of it. if-syntax-matters-then-syntax-matters-ly y'rs - tim

Tim Peters wrote:
Just picking on the loop issue here.
...
Yes I'm still ashamed on that. It may be that Guido was about to say "perhaps" (as worthy as a woman's "perhaps", you know), but I went too far and killed it by "and if". Sigh... SORRY!
Oh, much simpler: We have it all ready :) Just make "while 1:" into a keyword, like the #end block delimiters. ciao - chris -- Christian Tismer :^) <mailto:tismer@appliedbiometrics.com> Applied Biometrics GmbH : Have a break! Take a ride on Python's Düppelstr. 31 : *Starship* http://starship.python.net 12163 Berlin : PGP key -> http://wwwkeys.pgp.net PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF we're tired of banana software - shipped green, ripens at home

[about "and while", and generalization to "and if"] [Christian Tismer]
Don't apologize to me! I *thank* you -- "and while" is excessively novel (a phrase I'm using excessively often lately <wink>, but one that I think captures the things about Python I like least). The world has many conventional, perfectly clear ways to spell this kind of thing already. The only thing stopping Python from adopting one is Keyword Fear (which I share, but not to the extent of eternal paralysis <0.9 wink>).
It's a needless initial barrier to language acceptance, but more importantly it's plainly bad pedagogics to model a "loop and a half" via a construct that *says* "do this while true", i.e. do this forever. Not a disaster, but surely deserving of the "wart" Andrew gives it. at-heart-it's-a-simple-problem-with-a-simple-fix-ly y'rs - tim

Sort of. Slice objects can be created with the slice builtin. slice(1,10,2) is meant to be equivalent to 1:10:2, but only NumPy knows how to deal with slice objects in the getitem slot (use None to express 'omission' of an index). It's on the TODO list for 1.7 at this point, although if someone submits a working patch to 1.6, who knows... JPython does it right, I believe. --david

[Greg Wilson]
I guess you didn't try it:
To pass the hash test, it needs to have immutable contents as well. To my mind, the "can act as dictionary key" argument is a red herring. Tuples and lists may technically be almost the same thing, but I find I hardly ever need to ponder whether I should use a tuple or a list. Tuples are structures and lists are containers. Yes, you can use a container as a structure. You can also use a bucket as a spoon. or-stick-your-head-straight-into-the-trough-ly y'rs - Gordon

GmCm
[Greg Wilson]
4. "Aren't tuples redundant?"
They're not structures the way a C programmer thinks of a struct or a pascal programmer thinks of a record. Do you have a better way of justifying their existence to novices who haven't reached the zen? It's a nontrivial pedagogical problem in my experience as well. (yes, this is a better topic for edu-sig than for python-dev). --david

That doesn't matter; I'd like to assume that we want to teach students who don't already have a preconceived notion of a Pascal record or C struct.
I have a feeling that introducing optional static typing would help here, because it clarifies the concept. For example, these two have the same type, [int]: (the length is part of the value) [3, 100] [5, 6, 7] On the other hand, these two have different types: (3, 100) # (int, int) (5, 6, 7) # (int, int, int) If we start mixing types, we may get [3, "wow"] # [any]; or perhaps [int|str] (3, "wow") # (int, str) We can then explain that we might have informal names for the parts, and we can use these as local variable names. This is borrowed from ABC, which did at least *some* user testing on issues like this: count, value = t t = count, value (ABC used a different assignment syntax; it would be: PUT t IN count, value PUT count, value IN t I am still friends with the librarian who was taught ABC as a guinea pig.) However, ABC was a statically typed language in the sense of Haskell: there were no declarations but the types were inferenced from initializations and operators. Maybe that *does* make a difference. Also, its tuples were completely unlike sequences: they didn't have indexing or slicing. There were no tuples of lengths 1 or 0. --Guido van Rossum (home page: http://www.python.org/~guido/)

For CP4E, absolutely. I was just reporting on my experience with existing programmers not unlike Greg's (also with many fewer PhD's)[*].
I have a feeling that introducing optional static typing would help here, because it clarifies the concept.
Very interesting point -- however, I think that we need a better answer in general. The question in my experience stems from a desire to minimize memory load. The students grok lists in about 30 seconds. Then I explain tuples, and they wonder why they were "added" given that lists "exist" (in their brain at that time =). --david [*]: I did teach a course at Bank of America where 15 of the 16 people in the room were Vice Presidents. Of course, they were just programmers, but the pay for a competent programmer is high in the hierarchy of bank salaries. =)

[Guido]
Very much the same applies to functional languages, except (usually) even more so. For example, Haskell has both lists and tuples, but lists are homogenous (and the type system has no union types, at least not of the int|str form -- the list [3, "wow"] can't be expressed (although the tuple form can be): ? [3, "wow"] ERROR: [Char] is not an instance of class "Num" ? (a Haskell string is a list of Char, which is what [Char] means)). At heart, tuples are for Cartesian products of a fixed number of possibly differing types, while lists are for arbitrary-length sequences of a single type. This is good style even in Python! (Think about it: when you see a newbie post on c.l.py that violates your Python intuition wrt list or tuple use, in my experience it almost always violates one of those (hitherto never expressed <wink>) guidelines.) That lists are allowed to be heterogenous should really be considered an advanced feature; ditto list operations (like indexing) on tuples. BTW, despite the recent unpleasantness with the TeachScheme! folks, there's a lot to be learned from DrScheme, part of which is the notion of language "levels": the IDE can be set to restrict the newbie to various (nested) subsets of the full language, so that new learners are shielded from the hairier stuff until their confidence builds. metaclasses-are-a-good-candidate<wink>-ly y'rs - tim

I guess you didn't try it:
I actually had -- the point I was trying to make was that his first reaction was "But what about..." Maybe it's just a reflection of his Perl background :-). Like I said, you've all heard this one more times than I have... Greg

On Thu, 3 Feb 2000 gvwilson@nevex.com wrote:
Wow, these were really informative. Thanks for collecting the results.
1. "What's with 'while 1: break' for normal loops?"
I'd probably agree that this is a significant thing. I think it would be very nice to have do...while, and of all your examples i liked do: first-body while condition: second-body best. This very nicely takes care of the common idiom do: line = file.readline() while line: body (Once upon a time i even wrote a patch just to handle this case with a strange "while line from file.readline()" syntax. I think the above is much nicer.)
This *is* confusing. Does the "third-half" get executed if condition-2 is true, or only if condition-1 and condition-2 are both true? Hmmm... i suppose if i interpret each "and while <cond>" as "if not <cond>: break" it makes more sense. But i still like the first. "do" doesn't even have to be made into a keyword for the do...while construct to work: there is no other valid construct in which a name would appear followed by a colon as the first thing in a statement or expression, so the parser should be able to figure it out.
Wow, i like that. There is a nice symmetry to >>> t = (3, 7, 5, 6, 2, 1, 8) >>> print t[0:5] (3, 7, 5, 6, 2) >>> for i in [0:5]: print t[i] ... 3 7 5 6 2
I'd tend to agree with that. This is definitely one of the first things any beginner will see, and it's nice not to be sidetracked into an explanation of "range". The symmetry means that, after they've seen "for i in [0:10]:", they'll already have a feel for what "list[0:10]" ought to do. Oh, and having a slice object work in a "for" would allow us to drop that vaguely irritating range/xrange distinction. That said, however, i don't think i'd make a really big deal out of this. Maybe i'm being too swayed by the cuteness of the idea.
I was wondering why this doesn't work. Given that slices are built in, the lack of discussion of them in the documentation makes them rather mysterious. It would be nice to make them just do the expected thing. You could then do things like putting list[::-1] in an expression without having to copy it to a temporary, call reverse(), and then use the temporary.
-------------------------------------------------------------------------
3. "Where is '+=' ?" and "Why can't I redefine assignment?"
+= would be nice; not a really big deal to me. Redefining assignment would get way too confusing, i think. __setitem__ has always been good enough for me.
-------------------------------------------------------------------------
3.1 Min and Max
Interesting, but doesn't matter much to me.
-------------------------------------------------------------------------
3.2 Case Statement, and Remembering Matches in Conditionals
[...]
I like this quite a lot! One question: in that last "else" clause, wouldn't "x" be undefined?
-------------------------------------------------------------------------
5. "Why can't I put an 'except' after an 'if'?"
Possibly interesting, but it doesn't seem necessary to me. Having the "try:" at the beginning of the block more clearly delimits what's being protected. Wouldn't this look weird? if foo.bar > 0: blah else: blah except AttributeError: blah else: blah Now does the "except" cover just the first "else" or both the bodies of the "if" and the "else"? Alternatively, imagine that there used to be only an "if" and we wanted to insert an "else". It's clear we can't get rid of "try:" altogether, so it seems like more work to remember the specific list of other situations in which "except:" might apply...
-------------------------------------------------------------------------
6. "There is no number six."
And six is RIGHT OUT. min-sv�vare-�r-full-med-�lar-ly y'rs, -- ?!ng

The loop breaks out the first time it finds a false condition. I find it confusing as well, and wonder whether the extra generality would ever be used. I'd even want to put: do: first-half while cond: second-half in front of both novices and experts --- is this two loops, or one?
Might not be true in future, so probably dangerous to assume now. (Icon, anyone? :-) Thanks, Greg

Greg Wilson wrote: 3. "Where is '+=' ?" and "Why can't I redefine assignment?"
It'll make a big difference to NumPy (avoiding temporaries, etc.). Also hard to explain why you can redefine assignment to an array element, but not to a scalar variable. Thanks, Greg

FWIW, I never got that question by my students. I did get that question in a consulting context, but it was a very advanced problem having to do with needing control over references to huge external objects, etc. After much trial and error I've come up with a 'method' for describing assignment, references, names, etc., which I've found works well in practice. I demonstrate the concept of names & references on a whiteboard with trivial cases like a = 1 + 3 and then I move on to b = 3 c = 5 a = b + c and then a = [b,c] and then I graduate to the a = [0]*3 and then finally a = [[0]*3]*3 and when they get that I know I've succeeded in getting the concept of assignment in Python across. It's much too hard to describe in email because it requires a blackboard and drawing arrows, erasing arrows, etc., but someday I can demonstrate (SD2000?). Anyway, it's possible that your students are just too smart for their own good, Greg. =) --david

I wish I could tell from my notes whether the ones asking to redefine assignment had C++ in their background or not. Setting the consistency issue aside, libraries like NumPy (and the Python futures package I keep meaning to write) will be a lot cleaner from a user's point of view if assignment is redefinable. However, I do understand that there's no easy way to do it without taking an unacceptable performance hit... I'll send anyone who can come up with a clean, workable solution a case of Canadian beer (now how's *that* for a real prize? :-) Greg

(Quick flip through notes): "x is None in the else branch". Looking at it again, it came up as part of the question "Why isn't assignment an operator?" The student in question was used to doing: if (x = foo()) { body } else if (x = bar()) { body } else { body } and wanted to have something in Python that would (a) provide more flexibility than a C/C++ case statement, while (b) making it clear that the alternatives really were mutually exclusive. I think. Or maybe not. He talked really, really fast... Greg

Just picking on the loop issue here. [Ka-Ping Yee, on Greg Wilson's post]
Wow, these were really informative. Thanks for collecting the results.
Ditto! It's posts like these that make me glad Guido has so much spare time <wink>.
1. "What's with 'while 1: break' for normal loops?"
I'd probably agree that this is a significant thing.
Me too: it's the ugliest thing in Python newbies hit right away, and while *I* can't really "see" the convolution in "while 1: ... if xxx: break ..." anymore, that's learned behavior; but I get reminded of how grating it is at first each time someone new at work starts learning Python. This one made Andrew's "Python Warts" paper. Enough is enough already <0.5 wink>.
Actually suggested by Guido Himself on Christmas Eve of '98 (see DejaNews; I found the URL for Greg offline but have since lost it). And the IDLE editor already handles it fine <wink -- but that means pymode is likely to too, and so also "most things of that kind">.
This *is* confusing.
It's from an old c.l.py thread; don't recall who suggested it; I don't like it myself; "and while xxx:" is just sugar for "if not xxx: break"; its advantage is the absence of new keywords; the "while:" at the top reads poorly; the semantics of "and while" are indeed unclear at first (IIRC, Christian took way too many drugs at the time <wink> and tried to generalize it to "and if" as well ...).
Good point! Not the kind of point Guido warms to instantly, but a good point all the same. I had already checked, and there are at least two functions named "do" in Python's own std distribution. So making "do" a keyword is very unattractive. I'm not sure "repeat:" (a la Pascal) or "loop:" (Icon? too lazy to look it up -- *one* of those loser languages I'm always irritating people about <wink>) would actually be better. If hackery could overload "do" (or repeat, or loop), I'm in favor of it. if-syntax-matters-then-syntax-matters-ly y'rs - tim

Tim Peters wrote:
Just picking on the loop issue here.
...
Yes I'm still ashamed on that. It may be that Guido was about to say "perhaps" (as worthy as a woman's "perhaps", you know), but I went too far and killed it by "and if". Sigh... SORRY!
Oh, much simpler: We have it all ready :) Just make "while 1:" into a keyword, like the #end block delimiters. ciao - chris -- Christian Tismer :^) <mailto:tismer@appliedbiometrics.com> Applied Biometrics GmbH : Have a break! Take a ride on Python's Düppelstr. 31 : *Starship* http://starship.python.net 12163 Berlin : PGP key -> http://wwwkeys.pgp.net PGP Fingerprint E182 71C7 1A9D 66E9 9D15 D3CC D4D7 93E2 1FAE F6DF we're tired of banana software - shipped green, ripens at home

[about "and while", and generalization to "and if"] [Christian Tismer]
Don't apologize to me! I *thank* you -- "and while" is excessively novel (a phrase I'm using excessively often lately <wink>, but one that I think captures the things about Python I like least). The world has many conventional, perfectly clear ways to spell this kind of thing already. The only thing stopping Python from adopting one is Keyword Fear (which I share, but not to the extent of eternal paralysis <0.9 wink>).
It's a needless initial barrier to language acceptance, but more importantly it's plainly bad pedagogics to model a "loop and a half" via a construct that *says* "do this while true", i.e. do this forever. Not a disaster, but surely deserving of the "wart" Andrew gives it. at-heart-it's-a-simple-problem-with-a-simple-fix-ly y'rs - tim
participants (8)
-
Christian Tismer
-
David Ascher
-
David Ascher
-
Gordon McMillan
-
Guido van Rossum
-
gvwilson@nevex.com
-
Ka-Ping Yee
-
Tim Peters