Below is typical feedback to a student.
What do others think regarding my discussion of using try /except in routine dict manipulation? Kirby === Yes perfect. Good work. try: res_dict[ext] += 1 except KeyError: res_dict[ext] = 1 This code is not atypical and you'll find some polarization among Pythonistas about whether this is good style. Another construct: res_dict[ext] = res_dict.get(ext, 0) + 1 is the one I favor. I think of a "key not found" event in a dict as "routine, to be expected" whereas I think of try: / except: as for "crying wolf" (not for everyday contingencies). The opposite bias is: why make that distinction, try: / except: is a perfectly reasonable construct for trapping a new key to a dict. In any case, good Python. -Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
On 12/13/2011 04:30 PM, Kirby Urner wrote:
> I think of a "key not found" event > in a dict as "routine, to be expected" whereas I think of > try: / except: as for "crying wolf" (not for everyday > contingencies). The opposite bias is: why make that > distinction, try: / except: is a perfectly reasonable > construct for trapping a new key to a dict. > More evidence for the "perfectly reasonable": Iterators are implemented with the StopIteration exception. You can find a few other examples in the language for things like that. I think it's perfectly reasonable myself. -- Corey Richardson _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Kirby Urner-6
I enjoy your explanation. I have recently written something very similar to one of my students.
We teach dictionaries about three weeks before exception handling, though, so I rarely see this code.
On Tue, Dec 13, 2011 at 4:30 PM, Kirby Urner <[hidden email]> wrote: Below is typical feedback to a student. Sarina Canelake MIT EECS _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Corey Richardson
I'm encouraging the use of try: except around here. I think the
distinction between 'routine' and 'non-routine' only makes sense if exceptions are so terribly slower than the routine that using them for routine use will have a terrible effect in slowness on your program. It's like using: string1 = string1 + newstring inside a loop. The reason you don't do this is not because there is something conceptually wrong with this idea, but because it's horribly slow. And if smarter pythons recognise this, and emit fast code for it anyway, then that objection goes away, and then, well, it is a more straight forward way to code it. So, if performance is not an issue, then I would recommend using try: except: wherever it feels natural, and not let '20 years of knowing that in java exceptions are horrible big and slow things to be used sparingly' influence what feels natural. Laura _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Kirby Urner-6
Sorry, Kirby, I'm afraid I disagree.
try: res_dict[ext] += 1 except KeyError: res_dict[ext] = 1 is clear in intent and operation. It is a self-initializing occurrence counter. On the other hand, res_dict[ext] = res_dict.get(ext, 0) + 1 is obscure. Unless I go dig up the documentation of what the .get() operation does on a dictionary, I have no idea what the intention of the code is -- and I am (humbly) a very experienced programmer and Python programmer. Also I doubt whether the one-line version is faster. Without looking at the code, I would bet that .get() is implemented using a try / except structure. There is nothing wrong with that. Remember the Zen of Python: >>>import this ... Readability counts. ... -- Vernon _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
I think I'd go along with Laura and Vernon as long as it's made clear that one should specify the exception, as Vernon does in his sample. Bare excepts should NOT be encouraged, particularly since I've seen some students find them way too seductive - "Look, I can handle EVERY error!" ;)
OTOH the .get() method has always struck me as imposing a bit of a cognitive load that I could live without.
Vern On Wed, Dec 14, 2011 at 3:42 PM, Vernon Cole <[hidden email]> wrote: Sorry, Kirby, I'm afraid I disagree. Vern Ceder [hidden email], [hidden email] The Quick Python Book, 2nd Ed - http://bit.ly/bRsWDW _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
Interesting responses.
I harp on that somedict [ key ] = somedict.get( key, 0 ) + 1 option quite a bit, as shorter and easier than all that try / except stuff. That's just the bias I've been applying, to an invisible army of... what? Hundreds? Hard to keep track. Anyway, good to see the other side of things. I tend to bust out in try / except syntax when I have to, but if I can keep it more button-down, I do. Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Vernon D. Cole
On 14.12.2011, at 22:42, Vernon Cole wrote:
> Sorry, Kirby, I'm afraid I disagree. > try: > res_dict[ext] += 1 > except KeyError: > res_dict[ext] = 1 > is clear in intent and operation. It is a self-initializing occurrence counter. > > On the other hand, > res_dict[ext] = res_dict.get(ext, 0) + 1 > is obscure. Unless I go dig up the documentation of what the .get() > operation does on a dictionary, I have no idea what the intention of > the code is -- and I am (humbly) a very experienced programmer and > Python programmer. Also I doubt whether the one-line version is > faster. Without looking at the code, I would bet that .get() is > implemented using a try / except structure. There is nothing wrong > with that. > > Remember the Zen of Python: >>>> import this > ... > Readability counts. > ... > -- > Vernon Isn't this at least as readable, and conceptually simpler? if ext in res_dict: res_dict[ext] += 1 else: res_dict[ext] = 1 Not arguing against exceptions in general, but in this case? - Bert - _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
> Isn't this at least as readable, and conceptually simpler?
> > if ext in res_dict: > res_dict[ext] += 1 > else: > res_dict[ext] = 1 I get that a lot too and often propose my one-liner. This is not a matter of flagging a mistake, just showing another way. It's a chance to learn dict methods better and hey, they're paying to have a pro coach. Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In a message of Wed, 14 Dec 2011 14:36:43 PST, kirby urner writes:
>> Isn't this at least as readable, and conceptually simpler? >> >> if ext in res_dict: >> res_dict[ext] += 1 >> else: >> res_dict[ext] = 1 > >I get that a lot too and often propose my one-liner. This is not a >matter of flagging a mistake, just showing another way. > >It's a chance to learn dict methods better and hey, they're paying to >have a pro coach. > >Kirby This looks suspiciously like 'I am a superior being because I know more obscure methods that you do, and obscure knowledge defines the professional'. Bleah. By my standards, this is _unprofessional_. Whenever you have the urge to write code to show off how smart or how knowledgable you are, kick yourself. Professional code is meant to be read. If you make the general reader go off to the manuals to find out why something is done, then you ought to have a darn good reason for doing it that way than the way that everybody can read and understand. Personally, I think that Python would be a much better language if it had fewer of these sorts of methods, thus forcing people to use syntax like Bert Freudenberg posted above, because the more obscure ways aren't available. Now we have to rely on personal self-discipline to not use them, even when we know them, and a deep, unshakeable faith that code is meant, first and foremost to be _read_, and read by _everybody, not just the other 'experts' -- an expert being one who is defined here as 'one who is well-steeped in Python obscurity as you are'. If we loose the ability to read Python, because all the new graduates are writing code that is hard to read as a matter of principle -- because they think it is superior, or a matter of ego -- because it shows off their knowledge and maybe their cleverness, then I might as well be reading any other language. We'll have lost the primary thing that I think makes Python valuable, via the 'boil-the-frog' principle. http://en.wikipedia.org/wiki/Boiling_frog I think as educators it is our job to make sure this does not happen by teaching the appropriate self-discipline. I'd like to send your top students back their code with the comment, 'Great that it works. Now can you make it more readable?' I'd be looking for code like the above to replace code like yours. Laura _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
On Thu, Dec 15, 2011 at 12:08:48AM +0100, Laura Creighton wrote:
> In a message of Wed, 14 Dec 2011 14:36:43 PST, kirby urner writes: > >> Isn't this at least as readable, and conceptually simpler? > >> > >> if ext in res_dict: > >> res_dict[ext] += 1 > >> else: > >> res_dict[ext] = 1 > > > >I get that a lot too and often propose my one-liner. This is not a > >matter of flagging a mistake, just showing another way. > > > >It's a chance to learn dict methods better and hey, they're paying to > >have a pro coach. > > > >Kirby > > This looks suspiciously like 'I am a superior being because I know more > obscure methods that you do, and obscure knowledge defines the professional'. > Bleah. Kirby thinks the one-liner is more readable than the 4-liner. So do I. Apparently this is something upon which reasonable people can disagree. :) David H _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Kirby Urner-6
> Isn't this at least as readable, and conceptually simpler?
> > if ext in res_dict: > res_dict[ext] += 1 > else: > res_dict[ext] = 1 > > Not arguing against exceptions in general, but in this case? > > - Bert - Yes, it is readable -- but more resource intensive -- since I have to access the dictionary twice, once to see if the key is there, and another to adjust or define the value. If I use try / except, then I will usually only have to access the dictionary a single time, with a second dip only if the key did not exist. A clear win for using exceptions. Incidentally, the .get() one liner also does two dictionary accesses, rather than one (since it can't use the += operator, the get() and the update/define are separate operations.) "Less lines" does not necessarily mean "runs faster." Dictionary lookups are a somewhat expensive operations. -- Vernon _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Bert Freudenberg
On Wed, Dec 14, 2011 at 6:15 PM, Bert Freudenberg <[hidden email]> wrote:
On 14.12.2011, at 22:42, Vernon Cole wrote: SNIP Isn't this at least as readable, and conceptually simpler? Personally, I find both the if/else and the try/except much easier to remember, and slightly easier to read than the one-liner. Furthermore, if one is interested in performance, it appears that the if/else approach is *always* faster than the one-liner. When exceptions are rarely raised, then the try/except approach is the fastest. Below are the results from a quick test to compare the efficiency of the three cases, followed by the code. This is using Python 2.7 on a fairly old computer.
André =============================== 100% new keys setdefault: 0.532404899597
try/except: 2.029556036 if/else: 0.241212844849 --------------------------------------------------
10% new keys setdefault: 0.621008872986 try/except: 0.585212945938
if/else: 0.32776093483 -------------------------------------------------- 1% new keys setdefault: 0.615134000778
try/except: 0.388912916183 if/else: 0.321834087372 --------------------------------------------------
0.1% new keys setdefault: 0.527935028076 try/except: 0.28360915184
if/else: 0.334416866302 ================ from timeit import Timer print "100% new keys\n"
print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): d[i] = d.get(i, 0) + 1 """) print t.timeit(number=1000)
print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): try: d[i] += 1 except KeyError:
d[i] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000):
if i in d: d[i] += 1 else: d[i] = 1 """) print t.timeit(number=1000) print "-"*50
print "10% new keys" print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): k = i % 100 d[k] = d.get(k, 0) + 1
""") print t.timeit(number=1000) print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): k = i % 100
try: d[k] += 1 except KeyError: d[k] = 1 """) print t.timeit(number=1000) print 'if/else:\t',
t = Timer(""" d = {} for i in range(1000): k = i % 100 if i in d: d[k] += 1 else: d[k] = 1 """)
print t.timeit(number=1000) print "-"*50 print "1% new keys\n" print 'setdefault:\t', t = Timer(""" d = {}
for i in range(1000): k = i % 10 d[k] = d.get(k, 0) + 1 """) print t.timeit(number=1000) print 'try/except:\t',
t = Timer(""" d = {} for i in range(1000): k = i % 10 try: d[k] += 1 except KeyError: d[k] = 1 """)
print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000): k = i % 10 if i in d:
d[k] += 1 else: d[k] = 1 """) print t.timeit(number=1000) print "-"*50 print "0.1% new keys\n"
print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): d[1] = d.get(1, 0) + 1 """) print t.timeit(number=1000)
print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): try: d[1] += 1 except KeyError:
d[1] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000):
if 1 in d: d[1] += 1 else: d[1] = 1 """) print t.timeit(number=1000) _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Laura Creighton-2
On Wed, Dec 14, 2011 at 3:08 PM, Laura Creighton <[hidden email]> wrote:
> In a message of Wed, 14 Dec 2011 14:36:43 PST, kirby urner writes: >>> Isn't this at least as readable, and conceptually simpler? >>> >>> if ext in res_dict: >>> res_dict[ext] += 1 >>> else: >>> res_dict[ext] = 1 >> >>I get that a lot too and often propose my one-liner. This is not a >>matter of flagging a mistake, just showing another way. >> >>It's a chance to learn dict methods better and hey, they're paying to >>have a pro coach. >> >>Kirby > > This looks suspiciously like 'I am a superior being I know more > obscure methods that you do, and obscure knowledge defines the professional'. > Bleah. > I'll take the opposite view for the sake of argument. dict is like the core data structure in Python and if you don't know about dict.get( ) then good thing you came to the right place where professionals are not afraid to use the well-thought-out features of this language. To treat dict.get(k, d) as obscure and it make it one's professional business avoid it is misguided. > By my standards, this is _unprofessional_. Whenever you have the urge > to write code to show off how smart or how knowledgable you are, kick > yourself. Professional code is meant to be read. If you make the > general reader go off to the manuals to find out why something is > done, then you ought to have a darn good reason for doing it that way > than the way that everybody can read and understand. > I always explain with the syntax is doing. It's not about going off to the manuals. I'm shrinking four of five lines of code to one. Many of my students use dict get( ) naturally, without my prompting. > Personally, I think that Python would be a much better language if it > had fewer of these sorts of methods, thus forcing people to use syntax > like Bert Freudenberg posted above, because the more obscure ways > aren't available. Now we have to rely on personal self-discipline to > not use them, even when we know them, and a deep, unshakeable faith > that code is meant, first and foremost to be _read_, and read by > _everybody, not just the other 'experts' -- an expert being one who is > defined here as 'one who is well-steeped in Python obscurity as you are'. > I think returning a default if a key is not found is one of those workaday methods we thank Python for having, so we don't have to code up little mini-functions. If you saw try /except over and over and each time it was the same thing: some dict not having a key, so needing to initialize a tally versus incrementing, then pretty soon people'd be griping about why Guido didn't have the smarts to put something so basic in the dict toolkit. Hey here's an idea: Lets all be patronizing and use baby talk, so our students don't learn the basics in ways we were too lazy to. > If we loose the ability to read Python, because all the new graduates > are writing code that is hard to read as a matter of principle -- > because they think it is superior, or a matter of ego -- because it > shows off their knowledge and maybe their cleverness, then I might as > well be reading any other language. We'll have lost the primary thing > that I think makes Python valuable, via the 'boil-the-frog' principle. > http://en.wikipedia.org/wiki/Boiling_frog I think it's definitely a pitfall to be "too clever" in Python but where do we draw the line. Using built in methods that are basic to dict? I say what's *not* where to draw the line. I think better is to give a choice, show the options. It's my job to show off some of these basics in Beginning Python. Any method of any primitive data type is fair game. This isn't metaclasses or anything. > > I think as educators it is our job to make sure this does not happen by > teaching the appropriate self-discipline. I'd like to send your top > students back their code with the comment, 'Great that it works. Now can you > make it more readable?' I'd be looking for code like the above to replace > code like yours. > > Laura I think Python is called a very high level language for a reason, and if we think "readable Python" means pretending it's C or restricted PyPy type Python, then we're just ripping ourselves off and being proud for no good reason. Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by André Roberge
On Wed, Dec 14, 2011 at 08:59:58PM -0400, Andre Roberge wrote:
> Personally, I find both the if/else and the try/except much easier to > remember, and slightly easier to read than the one-liner. > > Furthermore, if one is interested in performance, it appears that the > if/else approach is *always* faster than the one-liner. When exceptions > are rarely raised, then the try/except approach is the fastest. Below are > the results from a quick test to compare the efficiency of the three cases, > followed by the code. This is using Python 2.7 on a fairly old computer. > > André (Snipped André's code and results.) Thanks for the benchmark! For readability, my very favorite is to use defaultdict, in Python since version 2.5: from collections import defaultdict d = defaultdict(int) d[k] += 1 I added a defaultdict case to André's benchmark. It definitely beats setdefault and is competitive with both try/except and if/else. This was also run using Python 2.7 on my very slow notebook PC. David H =============================== 100% new keys setdefault: 0.821501894794 defaultdict: 0.96327996994 try/except: 3.30088125725 if/else: 0.400257295271 -------------------------------------------------- 10% new keys setdefault: 1.07847561631 defaultdict: 0.656087296011 try/except: 0.980378791159 if/else: 0.600138006367 -------------------------------------------------- 1% new keys setdefault: 1.08054654991 defaultdict: 0.604530464067 try/except: 0.711149322051 if/else: 0.605315759405 -------------------------------------------------- 0.1% new keys setdefault: 0.848571815692 defaultdict: 0.395037078735 try/except: 0.482041712005 if/else: 0.570016859685 =============================== from timeit import Timer print "100% new keys\n" print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): d[i] = d.get(i, 0) + 1 """) print t.timeit(number=1000) print 'defaultdict:\t', t = Timer(""" d = defaultdict(int) for i in range(1000): d[i] += 1 """, setup='from collections import defaultdict') print t.timeit(number=1000) print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): try: d[i] += 1 except KeyError: d[i] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000): if i in d: d[i] += 1 else: d[i] = 1 """) print t.timeit(number=1000) print "-"*50 print "10% new keys" print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): k = i % 100 d[k] = d.get(k, 0) + 1 """) print t.timeit(number=1000) print 'defaultdict:\t', t = Timer(""" d = defaultdict(int) for i in range(1000): k = i % 100 d[k] += 1 """, setup='from collections import defaultdict') print t.timeit(number=1000) print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): k = i % 100 try: d[k] += 1 except KeyError: d[k] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000): k = i % 100 if i in d: d[k] += 1 else: d[k] = 1 """) print t.timeit(number=1000) print "-"*50 print "1% new keys\n" print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): k = i % 10 d[k] = d.get(k, 0) + 1 """) print t.timeit(number=1000) print 'defaultdict:\t', t = Timer(""" d = defaultdict(int) for i in range(1000): k = i % 10 d[k] += 1 """, setup='from collections import defaultdict') print t.timeit(number=1000) print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): k = i % 10 try: d[k] += 1 except KeyError: d[k] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000): k = i % 10 if i in d: d[k] += 1 else: d[k] = 1 """) print t.timeit(number=1000) print "-"*50 print "0.1% new keys\n" print 'setdefault:\t', t = Timer(""" d = {} for i in range(1000): d[1] = d.get(1, 0) + 1 """) print t.timeit(number=1000) print 'defaultdict:\t', t = Timer(""" d = defaultdict(int) for i in range(1000): d[1] += 1 """, setup='from collections import defaultdict') print t.timeit(number=1000) print 'try/except:\t', t = Timer(""" d = {} for i in range(1000): try: d[1] += 1 except KeyError: d[1] = 1 """) print t.timeit(number=1000) print 'if/else:\t', t = Timer(""" d = {} for i in range(1000): if 1 in d: d[1] += 1 else: d[1] = 1 """) print t.timeit(number=1000) _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by kirby urner-4
In a message of Wed, 14 Dec 2011 18:12:57 PST, kirby urner writes:
>I'll take the opposite view for the sake of argument. >I think returning a default if a key is not found is one of those >workaday methods we thank Python for having, so we don't have >to code up little mini-functions. If you saw try /except over and >over and each time it was the same thing: some dict not having >a key, so needing to initialize a tally versus incrementing, >then pretty soon people'd be griping about why Guido didn't >have the smarts to put something so basic in the dict toolkit. If you are seeing this over and over, then it is time to reach for defaultdict, no? I've found that that often does produce what is for me more readable code, and has the advantage of being very fast, too. But your last sentence here bothers me too. I think that 'smarts' and 'cleverness' are terribly overrated in society today. It's not that being stupid is an attractive option. But often the alternative to being _clever_ about something is being _wise_ about something. So the question, in that case, might not be 'why did Guido not have the smarts' but 'Was Guido being wise here?'. Is there a reason why this sort of thing does not exist? And, quite often, with questions of this sort you get the answer 'because there really isn't a need to clutter up the language with XXX' -- and you can look up past discussions about features that aren't in Python and see Guido saying exactly that all the time. The problem is that, after a certain stage in language development, you get beyond the point where every new feature you add is completely positive. Instead, you are stuck with haveing to decide for or against a feature that makes expressing 'this sort of problem' easier, and makes for clearer and more readable code, but at the expense of making it possible to use these constructs when dealing with 'that sort of problem', where the result is poorly constructed, harder to read code. And that is the tradeoff that is always there. Blindly trusting that a feature that is in a language should be used whenever it can be used, is not wise. But if we have a language for grown-ups, which relies on self-discipline, and indeed assumes that programmers have the wisdom to use Python well, then, well, there is nothing for it but as educators to try to promote wisdom. Suggestions on better ways to do this are most welcome. So far, being somewhat disapproving of cleverness, and downright hostile towards gratuitous cleverness is about the best I can do. As well as sending assignments back with the note -- refactor for redability -- which at least gets people thinking beyond 'how to solve this'. Laura _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
On Wed, Dec 14, 2011 at 7:38 PM, Laura Creighton <[hidden email]> wrote:
> If you are seeing this over and over, then it is time to reach for > defaultdict, no? I've found that that often does produce what is for me > more readable code, and has the advantage of being very fast, too. > I'm happy to see defaultdict enter the picture. That adds new grist for the mill. It's not covered in the lessons I teach from, so it wouldn't be as apropos, whereas students are much encouraged to explore the dict object using dir({}) etc. dict itself is an interesting builtin, being a type name and a callable, but not a function. dict( ) is not a function being called, likewise list( ), str( ), float( ), set( )... int( ). I'm getting off topic slightly. Also, I don't think fussing about a few microseconds is very Pythonic. If that's really your concern, then C might be better. VHLL means we're not anal about micoseconds. It's an interpreted language for crying out loud. "Readability" is the value I think we both agree on. It's more just a matter of where to draw the line. I'm thinking the dict's get( ) method, like itemsI( ), is too important to bleep over, especially when you see students using try: / except: syntax. Likewise, when introducing print( ), one must look at the optional keyword arguments, sep=, end= and file=. When you see a student laboriously trying to get around that default space between strings, let them know they have control. Here's what I come down to: at least make sure they know they have a choice. If they learn about .get and still go back to something else, fine. And that's indeed how I grade. I never send it back as "incorrect" if they didn't use .get (if it comes back, it's for other reasons) and I don't get bent out of shape if they decide to not incorporate it into their vocabulary, even after it's been introduced. At least I did my job in letting them know about it. > But your last sentence here bothers me too. I think that 'smarts' and > 'cleverness' are terribly overrated in society today. It's not that > being stupid is an attractive option. But often the alternative to > being _clever_ about something is being _wise_ about something. So > the question, in that case, might not be 'why did Guido not have the > smarts' but 'Was Guido being wise here?'. Is there a reason why this > sort of thing does not exist? > I happen to agree with your sentiments. I still think mytally [ mykey ] = mytally.get ( mykey, 0 ) + 1 is not "too clever". I think it's rather "just right". But sure, use defaultdict instead if you prefer. Or try / except, even though it's more verbose. What'd be a good lesson plan on defaultdict that lets it strut its stuff. I think we should incorporate that in one of our lessons. Who wants to do unpaid volunteer work. ;-) > And, quite often, with questions of this sort you get the answer 'because > there really isn't a need to clutter up the language with XXX' -- and > you can look up past discussions about features that aren't in Python > and see Guido saying exactly that all the time. > Which means he thinks dict.get is not clutter. I feel it's being there is evidence for my case in this debate. [ re this being "a debate" and me looking for a shared "value" -- readability -- is likely influenced by my immersing myself in the teen debate world in Washington / Oregon, and being suitably and happily impressed much of the time: http://worldgame.blogspot.com/2011/12/foray-to-washington.html ] > The problem is that, after a certain stage in language development, you > get beyond the point where every new feature you add is completely positive. > Instead, you are stuck with haveing to decide for or against a feature > that makes expressing 'this sort of problem' easier, and makes for clearer > and more readable code, but at the expense of making it possible to use > these constructs when dealing with 'that sort of problem', where the > result is poorly constructed, harder to read code. And that is the tradeoff > that is always there. > Maybe core Python should stop advancing. Seriously. Why keep going? It's a great language, 3 x best in show per LinuxJournal. Quit while ahead? Lets have cool libraries but call it quits on adding to core Python. Is anyone taking that position, just for kicks? I know about the moratorium that happened for awhile. Why not make it permanent? Wouldn't it be refreshing to have Python not evolve just so a lot more people could say they'd contributed a PEP? If you really can't live with the limitation of Python being how it is, start a new language. Note: I am mindful that PyPy provides many more freedoms and think halting the evolution of core Python would NOT inhibit people from forking off in all different directions with *specialized* Pythons that would be for particular lines of work, i.e. we could still have many many more Pythons. Just we're done defining "core" such that these would be self-labeling as "dialects" or "mutant forms". > Blindly trusting that a feature that is in a language should be used whenever > it can be used, is not wise. But if we have a language for grown-ups, which > relies on self-discipline, and indeed assumes that programmers have the > wisdom to use Python well, then, well, there is nothing for it but as > educators to try to promote wisdom. > > Suggestions on better ways to do this are most welcome. So far, being > somewhat disapproving of cleverness, and downright hostile towards > gratuitous cleverness is about the best I can do. As well as sending > assignments back with the note -- refactor for redability -- which at > least gets people thinking beyond 'how to solve this'. > > Laura > If someone used: mytally [ mykey ] = mytally.get ( mykey, 0 ) + 1 instead of one of the two other options presented, I would never suggest sending it back for readability. On the contrary, I'd say it's my job as a Python reader to know this idiom. This is a core method of a basic data type. However, I am persuaded by this thread, and it was also my position to begin with, that the two alternative ways of doing this same thing should not be sent back for refactoring either. I don't do this. I just present the above idiom and say things like "many programmers would abbreviate that to this". I usually quote their code and show the same .get( ) syntax with the exact same names, which means custom typing on my part, extra work (I graded 183 items today, many of them programs, so it's not like I have lots of time to spare). I don't think I'm going over the line in over-promoting superficial cleverness, which I agree is not to be encouraged except in contests / contexts where that's a pre-specified good / goal. Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In a message of Wed, 14 Dec 2011 23:01:54 PST, kirby urner writes:
>I happen to agree with your sentiments. > >I still think mytally [ mykey ] =3D mytally.get ( mykey, 0 ) + 1 >is not "too clever". I think it's rather "just right". But sure, >use defaultdict instead if you prefer. Or try / except, even >though it's more verbose. We're going to have to disagree about this one being 'just right' then. >> The problem is that, after a certain stage in language development, you >> get beyond the point where every new feature you add is completely positi >ve. >> Instead, you are stuck with haveing to decide for or against a feature >> that makes expressing 'this sort of problem' easier, and makes for clearer >> and more readable code, but at the expense of making it possible to use >> these constructs when dealing with 'that sort of problem', where the >> result is poorly constructed, harder to read code. And that is the tradeoff >> that is always there. >> > >Maybe core Python should stop advancing. Seriously. Why keep going? > >It's a great language, 3 x best in show per LinuxJournal. Quit while ahe >ad? > >Lets have cool libraries but call it quits on adding to core Python. > >Is anyone taking that position, just for kicks? > >I know about the moratorium that happened for awhile. Why not make it >permanent? Yes, this is pretty much my position. I don't think, in general, we can improve Python by making any more changes to the core language, and each change adds to the cognitive load we need to take on to learn the language. It used to take good employees 6 months to go from 'this is my first Python program' to being good Python programmers, and now it takes them longer. I'm not sure how much longer, or how we could go about measuring this, but I'd like to. But, again, I'm in favour of putting Software Transactional Memory into PyPy, and this will demand a syntax change. If the benefits of using STM happen -- we get to use our multi-cores for free -- then I think this would be a good candidate for a core language change. I hope that we will end up with the construct: with atomic: <these operations are executed atomically> see: http://morepypy.blogspot.com/2011/08/we-need-software-transactional-memory.html for more details. This is what I consider a new, very high level construct that would make Python a more high-level language than it is today. I'm vastly more sympathetic towards language changes like this, as oppsed to language changes of the 'grow a few more methods here and there'. I think that large new syntactical changes have to face much harder scrutiny, as to whether or not they are beneficial. But it's hard to argue against adding this method, and that one in particular. Each of them adds only a tiny bit of cognitive load. Thus I worry about the boiled frog effect. The other thing I worry about is that many people, for whatever reason, like making syntactic changes to programming languages. I'm guilty of this vice myself. I think that everybody should design computer languages, for fun, because there is a real joy in this. (Just don't inflict the rest of the world with your shiny new languages unless you really think that you have something new to offer.) What is worse, from this perspective, is that good number of people are only interested in programming in languages where a good number of syntax changes are happening all the time. If the changes stop happening, they abandon ship and move to another, more rapidly evolving place. Thus, even if we could reach consensus that Python was more or less finished, there will still be pressure to keep making changes (which some people would persist in calling evolving) out of fear that we will loose these people. I think that the nicest thing about the moratorium was how few people left because of it, and that, when it was over there wasn't a huge surge in pent up demand as people began agitating for hundreds of changes, those they had been sitting on since the ban began. This says to me that a good number of changes are things that people really could and did live without. Or maybe those who only want to be using cutting edge languages have jumped ship a long time ago, and are programming in Clojure or something, and we just didn't notice. >Wouldn't it be refreshing to have Python not evolve just so a lot more >people could say they'd contributed a PEP? I don't follow this. I'm opposed to making changes in the language so that more people can say that their PEPs were accepted, but I don't think that this is what you were proposing either. >If you really can't live with the limitation of Python being how it >is, start a new language. Yes. This is what Dennis Ritchie used to say when people proposed changes to C after a certain amount of time. I think this was very good for C. >Note: I am mindful that PyPy provides many more freedoms and think halting >the evolution of core Python would NOT inhibit people from forking off in >all different >directions with *specialized* Pythons that would be for particular >lines of work, i.e. >we could still have many many more Pythons. Just we're done defining "co >re" >such that these would be self-labeling as "dialects" or "mutant forms". The architectural problem that looms here is 'when should XXX be part of the core language' vs 'XXX should be part of a library'. And our past says 'for reasons of performance, we need to grow the core library, even if for other reasons we would prefer not to do so'. I think that this assumption is now being undermined by the new multi-core architectures we have, but only time will tell. >I usually quote their code and show the same .get( ) syntax >with the exact same names, which means custom typing >on my part, extra work (I graded 183 items today, many of >them programs, so it's not like I have lots of time to spare). Oh, I deeply sympathise with this problem. Suggestions of ways to automate this process are _also_ very welcome here. I'm getting seriously involved in automated testing of students these days, ways that students from remote locations can submit work online to be evaluated. (There are no grades here.). I had hoped that the ability to cut and paste would reduce my workload. It hasn't worked out that way. Possibly this is because I end up using every minute I save to say something that I would otherwise have skipped, regretfully, with the notion that it was too bad that I didn't have time to mention ... >I don't think I'm going over the line in over-promoting >superficial cleverness, which I agree is not to be encouraged >except in contests / contexts where that's a pre-specified >good / goal. Well, that's really all I wanted to hear. :-) If you can stand up to your own self-scrutiny, which I know is competant, then you probably aren't promoting shallow cleverness. But we are going to have to disagree about the readability of the get method. Laura > >Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
I'm not sure we are clear on the audience here. I use Python in teaching in different situations. For the newbie course with my Hands-on Python Tutorial, the main idea is an intro to creative programming, where many students will not get to anything more advanced. Python is a great vehicle, but my emphasis is not on how much of the language an advanced programmer is going to use. The dict get method is not high on my list in the newbie class. In that situation I would be drawn to the if key in ... syntax.
Thanks to Andre for his practical data. Getting real data like that fits in two situations:
Neither of these apply if you are just doing an intro to basic logical concepts in programming. When I use Python for my upper level cryptography course, I push way more advanced approaches.
Andy On Thu, Dec 15, 2011 at 7:05 AM, Laura Creighton <[hidden email]> wrote:
Dr. Andrew N. Harrington Computer Science Department Loyola University Chicago Lakeshore office in the Math Department: 205 Loyola Hall http://www.cs.luc.edu/~anh Phone: <a href="tel:773-915-7999" value="+17739157999" target="_blank">773-915-7999 Fax: <a href="tel:312-915-7998" value="+13129157998" target="_blank">312-915-7998 [hidden email] _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
In reply to this post by Laura Creighton-2
On Thu, Dec 15, 2011 at 5:05 AM, Laura Creighton <[hidden email]> wrote:
<< snip >> > We're going to have to disagree about this one being 'just right' then. > That's OK. Another attitude / realization I tried to get across in the top thread of my post is: schools of thought exist within the Python community. "you'll find some polarization among Pythonistas about whether this is good style" I said. "Polarization" is a good word because we know you can't have left without right, up without down -- they define one another. To take another probably less controversial example, I believe PEP8 or one of those encourages always triple quoting docstrings. If I notice students making assertions like "doctstrings must be triple quoted" I go: """no, you *may* consistently always triple quote even single-lined strings, but unless you have those embedded linebreaks, "single quoted docstrings" are not an impossibility -- not a syntax error, not an oxymoron""" (that's a paraphrase; sounding more literal given I'm not plowing through at high speed here). I'm guessing few of us here always triple quote our docstrings. > > with atomic: > <these operations are executed atomically> > > see: http://morepypy.blogspot.com/2011/08/we-need-software-transactional-memory.html > for more details. > This whole discussion was enlightening / educational, re additions to core Python. I'm tempted to change the subject line and do a branch on this. A proposal consistent with a permanent moratorium would be: yes, many dialects of Python, variants of Core, include the "with atomic" syntax, and you'll want one of those if doing multi-core programming -- unless of course you want to try PythonX by the team at Y, still experimental but.... etc. In other words, just because Core Python is innocent of the multi-core environment, doesn't mean we can't have five Not-Core Pythons that deal with it intelligently. Core Python becomes frozen, a kind of fossil, but there's just the one of them, the superclass. Then you have subclasses of Python-the-language that inherit and hearken back, but don't claim themselves to be Core anymore. No more fights. Just Diversity. You might see where I'm going with this: a version of Python with all keywords in Farsi for example. It's actually Core but with this one change. Rather than say we're running it through a translator, we're saying we have at least 300 new Pythons by the end of 2015, each in a different unicode language (when it comes to keywords). Of course many would decry this availability of multi- language Pythons and say it detracts from the universal readability of the One ASCII / English Core Version. But that's jumping the gun. Maybe only a tiny ethnic subculture of Farsi speakers use the Farsi one and so on, which the super-vast majority continue with the English core. So what? I'm just saying: it'd be useful to have the idea of "dialects" or "versions" well developed on many axes, not just one-core / multi-core. Kirby _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig |
Free forum by Nabble | Edit this page |