![](https://secure.gravatar.com/avatar/61a537f7b31ecf682e3269ea04056e94.jpg?s=120&d=mm&r=g)
Thank you for writing this PEP, Chris. I'm impressed by the quality of this PEP, and how you handled the discussion on python-ideas. I initially liked this idea, however, after reading the PEP in detail, my vote is: -1 on the current syntax; -1 on the whole idea. On 2/20/2014, 10:15 PM, Chris Angelico wrote:
[snip] * dict.get(key, default) - second positional argument in place of KeyError
* next(iter, default) - second positional argument in place of StopIteration
* list.pop() - no way to return a default We can fix that in 3.5. * seq[index] - no way to handle a bounds error We can add 'list.get(index, default)' method, similar to 'Mapping.get'. It's far more easier than introducing new syntax.
* min(sequence, default=default) - keyword argument in place of ValueError
* sum(sequence, start=default) - slightly different but can do the same job 'sum' is not a very frequently used builtin.
I think the Motivation section is pretty weak. 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 want to see this (or any other syntax) used by anyone. I also searched how many 'except IndexError' are in the standard library code. Around 60. That's a rather low number, that can justify adding 'list.get' but not advocate a new syntax. Moreover, I think that explicit handling of IndexError is rather ugly and error prone, using len() is usually reads better.
[snip]
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.
Retrieve an argument, defaulting to None:: cond = args[1] except IndexError: None
# Lib/pdb.py:803: try: cond = args[1] except IndexError: cond = None
Attempt a translation, falling back on the original:: e.widget = self._nametowidget(W) except KeyError: W
# Lib/tkinter/__init__.py:1222: try: e.widget = self._nametowidget(W) except KeyError: e.widget = W I'm not sure this is a good example either. I presume '_nametowidget' is some function,
cond = None if (len(args) < 2) else args[1] that might raise a KeyError because of a bug in its implementation, or to signify that there is no widget 'W'. Your new syntax just helps to work with this error prone api.
Read from an iterator, continuing with blank lines once it's exhausted:: line = readline() except StopIteration: ''
# Lib/lib2to3/pgen2/tokenize.py:370: try: line = readline() except StopIteration: line = ''
Handling StopIteration exception is more common in standard library than IndexError (although not much more), but again, not all of that code is suitable for your syntax. I'd say about 30%, which is about 20-30 spots (correct me if I'm wrong).
Retrieve platform-specific information (note the DRY improvement); this particular example could be taken further, turning a series of separate assignments into a single large dict initialization:: # sys.abiflags may not be defined on all platforms. _CONFIG_VARS['abiflags'] = sys.abiflags except AttributeError: ''
# Lib/sysconfig.py:529: try: _CONFIG_VARS['abiflags'] = sys.abiflags except AttributeError: # sys.abiflags may not be defined on all platforms. _CONFIG_VARS['abiflags'] = ''
Retrieve an indexed item, defaulting to None (similar to dict.get):: def getNamedItem(self, name): return self._attrs[name] except KeyError: None
# Lib/xml/dom/minidom.py:573: def getNamedItem(self, name): try: return self._attrs[name] except KeyError: return None _attrs there is a dict (or at least it's something that quaks
Ugly. _CONFIG_VARS['abiflags'] = getattr(sys, 'abiflags', '') Much more readable. like a dict, and has [] and keys()), so return self._attrs.get(name)
Translate numbers to names, falling back on the numbers:: g = grp.getgrnam(tarinfo.gname)[2] except KeyError: tarinfo.gid u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: tarinfo.uid
# Lib/tarfile.py:2198: try: g = grp.getgrnam(tarinfo.gname)[2] except KeyError: g = tarinfo.gid try: u = pwd.getpwnam(tarinfo.uname)[2] except KeyError: u = tarinfo.uid This one is a valid example, but totally unparseable by humans. Moreover, it promotes a bad pattern, as you mask KeyErrors in 'grp.getgrnam(tarinfo.gname)' call.
Perform some lengthy calculations in EAFP mode, handling division by zero as a sort of sticky NaN::
value = calculate(x) except ZeroDivisionError: float("nan")
try: value = calculate(x) except ZeroDivisionError: value = float("nan")
Calculate the mean of a series of numbers, falling back on zero::
value = statistics.mean(lst) except statistics.StatisticsError: 0
try: value = statistics.mean(lst) except statistics.StatisticsError: value = 0
I think all of the above more readable with try statement.
Retrieving a message from either a cache or the internet, with auth check::
logging.info("Message shown to user: %s",((cache[k] except LookupError: (backend.read(k) except OSError: 'Resource not available') ) if check_permission(k) else 'Access denied' ) except BaseException: "This is like a bare except clause")
try: if check_permission(k): try: _ = cache[k] except LookupError: try: _ = backend.read(k) except OSError: _ = 'Resource not available' else: _ = 'Access denied' except BaseException: _ = "This is like a bare except clause" logging.info("Message shown to user: %s", _)
If you replace '_' with a 'msg' (why did you use '_'??) then try statements are *much* more readable.
[snip]
Lib/ipaddress.py:343:: try: ips.append(ip.ip) except AttributeError: ips.append(ip.network_address) Becomes:: ips.append(ip.ip except AttributeError: ip.network_address) or it may become:
ips.append(getattr(ip, 'ip', ip.network_address)) or address = getattr(ip, 'ip', ip.network_address) ips.append(address) --- All in all, your proposal scares me. I doesn't make python code readable, it doesn't solve the problem of overbroad exceptions handling (you have couple examples of overbroad handling in your PEP examples section). Yes, some examples look neat. But your syntax is much easier to abuse, than 'if..else' expression, and if people start abusing it, Python will simply loose it's readability advantage. Yury