My stupidity / strange inconsistency overriding class methods
clp2 at rebertia.com
Wed Apr 20 02:45:42 CEST 2011
On Tue, Apr 19, 2011 at 4:52 PM, andrew cooke <andrew at acooke.org> wrote:
> I've been staring at this problem, in various forms, all day. Am I missing something obvious, or is there some strange hardwiring of isinstance? This is with Python 3.2.
> class A(metaclass=ABCMeta):
> def __instancecheck__(cls, instance): return False
> # no override
> assert isinstance(A(), A)
> assert A.__class__.__instancecheck__(A, A())
[You've already figured out the issue, but since I spent a while
composing this, and for the benefit for the archives, I'll post
Makes sense after a little thought.
"Note that [ __instancecheck__() is ] looked up on the type
(metaclass) of a class. [It] cannot be defined as [a classmethod] in
the actual class. This is consistent with the lookup of special
methods that are called on instances, only in this case the instance
is itself a class."
Recall from http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes
that lookup of __special__ methods never consults instance
dictionaries, instead skipping directly to the type's namespace; as
the quote says, in this case, the instance (of ABCMeta) is itself a
class/type (namely A). Your two assert statements are therefore almost
precisely equivalent in this case; and since the latter involves
A.__class__ (a.k.a. ABCMeta) rather than A itself, it's understandable
that that A's namespace is not consulted.
> class B(type):
> def foo(self): return 42
> class C(metaclass=B):
> def foo(cls): return 7
> # override
> assert C().__class__.foo() == 7
More simply: assert C.foo() == 7
"foo" is not a __special__ method name; therefore we look in the
instance dictionary of the receiver (i.e. C) before consulting the
receiver's type (i.e. B). Our check in the instance dictionary is
successful (we find C.foo), and therefore we don't even bother looking
at C's type (i.e. B, where we would find B.foo).
> It seems to me that the above two cases are inconsistent. ABCMeta declares __instancecheck__ just like B declares foo. Yet C can override foo, but A is unable to override the instance check.
The difference is in the __special__-ness of the method names in question.
More information about the Python-list