[Python-Dev] PEP 463: Exception-catching expressions
Steven D'Aprano
steve at pearwood.info
Thu Mar 6 02:15:38 CET 2014
On Wed, Mar 05, 2014 at 12:57:03PM -0800, Thomas Wouters wrote:
> On Thu, Feb 27, 2014 at 1:29 PM, Chris Angelico <rosuav at gmail.com> wrote:
>
> > +Had this facility existed early in Python's history, there would have been
> > +no need to create dict.get() and related methods;
>
>
> FWIW, after experimenting and some consideration I've come to the
> conclusion that this is incorrect. 'd[k] except KeyError: default' is still
> much broader than dict.get(k):
I don't think your example proves what you think it does. I think it
demonstrates a bug in the dict.get method. The documentation for get
states clearly that get will never raise KeyError:
Return the value for key if key is in the dictionary, else default.
If default is not given, it defaults to None, so that this method
never raises a KeyError.
http://docs.python.org/3/library/stdtypes.html#dict.get
but your example demonstrates that in fact it can raise KeyError
(albeit under some rather unusual circumstances):
> Python 3.4.0rc1+ (default:aa2ae744e701+, Feb 24 2014, 01:22:15)
> [GCC 4.6.3] on linux
> Type "help", "copyright", "credits" or "license" for more information.
> >>> expensive_calculation = hash
> >>> class C:
> ... _hash_cache = {}
> ... def __init__(self, value):
> ... self.value = value
> ... if value not in self._hash_cache:
> ... self._hash_cache[value] = expensive_calculation(value)
> ... def __hash__(self):
> ... return self._hash_cache[self.value]
> ... def __eq__(self, other):
> ... return self.value == other
> ...
> >>> a, b, c, d = C(1), C(2), C(3), C(4)
> >>> D = {a: 1, b: 2, c: 3, d: 4}
> >>> a.value = 5
> >>> print("except expr:", (D[a] except KeyError: 'default'))
> except expr: default
> >>> print("dict.get:", D.get(a, 'default'))
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 8, in __hash__
> KeyError: 5
According to the documentation, this behaviour is wrong.
Now, you might argue that the documentation is wrong. I'm sympathetic to
that argument, but *as documented now*, dict.get is documented as being
logically equivalent to:
try:
return d[key]
except KeyError:
return default
The fact that it actually isn't is an artifact of the specific
implementation used. If it were a deliberate design choice, that design
is not reflected in the documentation.
Whether the current behaviour is wrong, or the documentation is wrong,
is irrelevant to the question of whether or not the developers back in
nineteen-ninety-whatever would have choosen to add dict.get had there
been syntax for catching the KeyError in an expression. Perhaps they
would have argued:
"Sure, you can catch the KeyError yourself, but 'get' is a fundamental
operation for mappings, and I think that dict should implement a 'get'
method just to be complete."
Or perhaps not. Some developers prefer minimalist APIs, some developers
prefer more exhaustive APIs.
Regardless of what might have happened back in 199x when dict.get was
first discussed, I think we can agree that an except expression will
lower the pressure on Python to add *more* "get-like" methods, or add
default arguments, in the future.
--
Steven
More information about the Python-Dev
mailing list