My stupidity / strange inconsistency overriding class methods

A

andrew cooke

Hi,

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):
@classmethod
def __instancecheck__(cls, instance): return False
# no override
assert isinstance(A(), A)
assert A.__class__.__instancecheck__(A, A())

class B(type):
def foo(self): return 42
class C(metaclass=B):
@classmethod
def foo(cls): return 7
# override
assert C().__class__.foo() == 7

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.

Please help!

Thanks,
Andrew
 
C

Chris Rebert

Hi,

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):
           @classmethod
           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
anyway.]

Makes sense after a little thought.
http://docs.python.org/reference/datamodel.html#customizing-instance-and-subclass-checks
"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):
           @classmethod
           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.

Cheers,
Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top