On Fri, Feb 21, 2014 at 02:04:45PM -0500, Yury Selivanov wrote:
Inconvenience of dict[] raising KeyError was solved by introducing the dict.get() method. And I think that
dct.get('a', 'b')
is 1000 times better than
dct['a'] except KeyError: 'b'
I don't think it is better. I think that if we had good exception-catching syntax, we wouldn't need a get method. "get" methods do not solve the problem, they are shift it. Every class that defines [] look-ups also needs a get method. We can write dict.get(key, default), but we can't write list.get(index, default). If we "fix" list, we then find that tuple.get(index, default) fails. So we "fix" tuple, and then discover that str.get(index, default) fails. If Python were Ruby, then all sequences could inherit from one Sequence class that defines: def get(self, index, default=None): try: return self[index] except IndexError: return default and similarly for mappings, with KeyError instead of IndexError. But this is Python, not Ruby, and there is no requirement that sequences must inherit from the same parent class. They just have to provide the same duck-typed interface. Having added a get method to every mapping and sequence, you then call list.pop() and discover that it too needs a default. And so on, forever. We're forever chasing the next method and function, adding default parameters to everything in sight. The latest example is max and min. Then there are functions or methods which come in pairs, like str.find and str.index. From time to time people ask for a version of list.index that returns a default value instead of raising. Should we give them one? I don't think Python is a better language by adding complexity into the libraries and built-ins in this way. (And let's not forget that while methods and functions can have default parameters, expressions cannot. So there is a whole class of potential operations that can never be given a default, because they aren't a function call.) The essence of programming is combining primitive constructs to make more complicated ones, not in the proliferation of special-purpose methods or functions. "get" is less primitive than normal [] lookup, and it exists for a special purpose: to avoid needing to break apart what ought is a single conceptual expression into a multi-line statement. Consider a single conceptual chunk of code, the expression process(mydict[key]). In the past, if you wanted to use a default value if the key didn't exist, the only alternative was to break that expression up into two statements, regardless of whether you used LBYL or EAFP idioms: if key in mydict: tmp = mydict[key] else: tmp = default result = process(tmp) try: tmp = mydict[key] except KeyError: tmp = default result = process(tmp) Consequently, to express a single expression *as a single expression*, we needed an expression that can do the same thing, and in the past that had to be a method: result = process(mydict.get(key, default)) But now we have a LBYL expression form: result = process(mydict[key] if key in mydict else default) and are suggesting a EAFP form: result = process(mydict[key] except KeyError: default) If Python had this syntax 20 years ago, would dicts have grown a get method? I doubt it very much. People would be told to wrap it in an except expression.
Consider this example of a two-level cache:: for key in sequence: x = (lvl1[key] except KeyError: (lvl2[key] except KeyError: f(key))) # do something with x
I'm sorry, it took me a minute to understand what your example is doing. I would rather see two try..except blocks than this.
It helps to break it up more appropriately. (Also less cryptic names.) I can see PEP 8 will need some changes if this syntax is approved. for key in sequence: x = (cache1[key] except KeyError: (cache2[key] except KeyError: f(key))) # Do something with x This proposal is not about saving lines of code, and if examples are written jammed into a single line or two, and that hurts readability, they should be formatted better. It is about expressing the EAFP idiom in an expression, without giving up the useful property that the "except" clause is only evaluated when needed, not in advance. (Chris, I think that ought to go in the motivation section of the PEP.) -- Steven