Author abarnert
Recipients abarnert, gvanrossum, martin.panter, ncoghlan, r.david.murray, serhiy.storchaka
Date 2016-01-04.20:43:09
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
> I propose to solve the narrow problem by indeed supporting the setting of certain special methods to None similar to __hash__. This should be limited to those methods for which this adds value, e.g. where the complete absence of the method causes a fall-back to a different API (e.g. __getitem__+__len__ in the case of __reversed__) -- the None value should just block the fallback

I believe that currently, except for the minor problem in #25864, you can set any special method to None and it _will_ block any fallback (and also block inheritance, of course, the reason it's used with __hash__):

 * __iter__ and __reversed__ directly fall back to another method.

 * __contains__ falls back to iterating, which will indirectly fall back on __iter__ (or on __getitem__, of course).

 * __iadd__ and friends fall back on __add__ and friends.

 * __add__ and friends fall back on __radd__ (on the other operand, but typically it will be of the same type).

 * __lt__ and friends are similar to __add__.

 * __getattr__ and friends are a pile of special cases.

 * I think everything else doesn't have a fallback, and would just raise a TypeError anyway--although setting to None is still useful as a way to block inheritance, as used for __hash__.

In all cases, if the primary method exists but is None, the implementation will try to call it, and let the TypeError from NoneType.__call__ escape, which blocks the fallback. (And, of course, it blocks the MRO search for an inherited method.)

Of course the message is the useless and cryptic "'NoneType' object is not callable" rather than the nice one you get from hash. But technically, it's all already working as desired.

We probably need to document that setting special methods to None blocks any fallback implementation (both for people who write such classes, and to ensure that other Pythons do the same thing as CPython). 

Practically, I think we need to improve the error message for the ones that are likely to come up, but probably not for all of them. I think this means either just reversed, or reversed+iter, or maybe reversed+iter+in. (Plus hash, of course, but that code is already there.)

Also, I suppose we should have tests for this behavior.

> This will require some changes in the ABCs that test for the presence of a given method (Iterable/__iter__, Container/__contains__, Reversible/__reversed__ -- the latter ABC should be added) and in implementations such as reversed(), iter() and 'in'. Maybe we should just do this for every ABC in (plus Reversible) that has a __subclasshook__ that tests for the presence of the special method.

Now that I think about it, I'm +0.5 on changing all of them. Imagine, as a reader, trying to guess why some do the check and some don't.

Also, I think they should test for None instead of falsiness like Hashable does. (Hopefully nobody ever writes something that's falsey but callable, but why tempt fate?)


I'll write a patch that expands the #25864 patch:

 * added (as in #25987).

 * All* subclass hooks check for None.

 * Mapping.__reversed__ = None.

 * One paragraph added to datamodel docs.

 * iter, reversed, and 'in' operator all raise a custom TypeError when the special method is None the same way as hash (but everything else will continue to implicitly raise the ugly TypeError).

 * Unit tests for None blocking fallback on all special methods I can think of (treating all the __ispam__, add the __rspam__, all inheritance except for __hash__, etc. as a single method each, rather than copying-pasting a half-dozen times).
Date User Action Args
2016-01-04 20:43:10abarnertsetrecipients: + abarnert, gvanrossum, ncoghlan, r.david.murray, martin.panter, serhiy.storchaka
2016-01-04 20:43:10abarnertsetmessageid: <>
2016-01-04 20:43:10abarnertlinkissue25958 messages
2016-01-04 20:43:09abarnertcreate