[Python-Dev] PEP 463: Exception-catching expressions
Terry Reedy
tjreedy at udel.edu
Thu Mar 6 04:29:44 CET 2014
On 3/5/2014 8:15 PM, Steven D'Aprano wrote:
> 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]
This is a buggy special method. According to the docs for hash and
__hash__ and the general convention on exceptions, a __hash__ method
should return an int or raise TypeError.
>> ... 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
This breaks the implied C invariant and makes the object 'a' incoherent
and buggy
>>>>> 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.
One could argue that an error raised in a special method is not raised
*by* a function that uses the special method. The docs constantly assume
that special methods are coded correctly.
'''bool([x])
Convert a value to a Boolean, using the standard truth testing
procedure. If x is false or omitted, this returns False; otherwise it
returns True.'''
... unless x.__bool__ raises or returns something other than True/False
-- in which case bool itself raises.
TypeError: __bool__ should return bool, returned int
> 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
It appears to be actually equivalent to
key_hash = hash(key)
try:
return d._hashlookup(key_has)
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,
Given that the choice is that bugs in special methods should not pass
silently, I would presume that it is intentional.
> that design is not reflected in the documentation.
The docs generally describe behavior in the absence of coding errors.
--
Terry Jan Reedy
More information about the Python-Dev
mailing list