On Sun, Oct 28, 2018 at 05:24:43AM +1100, Chris Angelico wrote:
On Sun, Oct 28, 2018 at 5:03 AM Joy Diamond <python.gem@gmail.com> wrote:
NOTE: As an optimization, isinstance(object, classinfo) does NOT call classinfo.__instancecheck__(instance) when type(object) == classinfo.
I'd like to discuss this optimization. It seems very strange to me that a method designed specifically to override the isinstance check isn't actually called to allow it to override the isinstance check. [Chris]
Here's the passage in question, for reference:
""" The following methods are used to override the default behavior of the isinstance() and issubclass() built-in functions.
In particular, the metaclass abc.ABCMeta implements these methods in order to allow the addition of Abstract Base Classes (ABCs) as “virtual base classes” to any class or type (including built-in types), including other ABCs. """ https://docs.python.org/3/reference/datamodel.html#customizing-instance-and-...
Since it uses the word "override", I agree that it's not entirely correct.
Is that a polite way of saying "wrong"? The question we should be asking, is the optimization implementing the desired behaviour: * classes can disown instances of subclasses * but they cannot disown their own instances or is the optimization over-zealous and does too much? I don't think it is obvious that the behaviour is correct. Presumably Joy had a use-case for overriding isinstance(), and this optimization prevented it. Joy, can you comment on your use-case, and did you come up with a work-around?
The implication of "override" is that you can completely replace the normal behaviour.
Indeed.
In this case, you can change the behaviour of subclass testing (for instance, you can "disown" a subclass by denying that instances of it are instances of yourself), and of course, you can claim an object as an instance of a class it didn't directly inherit from (the way ABCs work), but you cannot fib about direct instances.
But we can change the class of direct instances, by changing their __class__ attribute, and that is intentional, supported behaviour. So "fibbing" is allowed. Perhaps changing the __class__ is enough to work-around this optimization, and there's nothing to do here except to document it better. But I think that if it is useful to own a non-instance, we shouldn't be so blasé about prohibiting disowning an instance.
I think the behaviour is close enough to accurate that it doesn't need major rewording; how about adding this parenthesis:
""" (Note that any object `x` is always considered to be an instance of `x.__class__`, and this cannot be overridden.) """
I would rather be precise about what is going on, and state that X.__instancecheck__(x) is not called if type(x) is X, rather than merely imply it. It is not just that the method is called and ignored, but that it isn't called at all. I find the process of checking types rather opaque and mysterious. Do we have a documented (other than the source) algorithm for deciding what is an instance of what? - type(x) and x.__class__ don't necessarily agree; under what circumstances are each used? (I've asked this before, and either never got a good answer, or I can't keep it straight in my head.) - what precisely does type(x) do? - when is __instancecheck__ called? A flowchart would be good :-) -- Steve