This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author steven.daprano
Recipients Gabriele Tornetta, bup, ethan.furman, paul.moore, r.david.murray, rhettinger, steven.daprano
Date 2021-12-10.11:41:23
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1639136483.47.0.941159479942.issue32683@roundup.psfhosted.org>
In-reply-to
Content
The plot thickens.

I was wrong to say that type() always and only looks at the "real" underlying type of the instance. type() does look at __class__ as well. But only sometimes.

>>> class A:
...     __class__ = int
... 
>>> type(A())
<class '__main__.A'>
>>> a = A()
>>> a.__class__ = int
>>> type(a)
<class '__main__.A'>

So from A, we might think that type() ignores __class__

>>> class B:
...     pass
... 
>>> type(B())  # no __class__ to inspect
<class '__main__.B'>
>>> b = B()
>>> b.__class__ = int
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ assignment only supported for mutable types or ModuleType subclasses

How very odd. But okay, let's try something else:

>>> b.__class__ = A  # lie that this is an A not a B
>>> type(b)
<class '__main__.A'>

So now type() *does* inspect __class__, and believes it.

If we generate the __class__ attribute dynamically with __getattr__, the method doesn't even get called. But if we use __getattribute__:

>>> class C:
...     def __getattribute__(self, name):
...             print('C getattribute:', name)
...             if name == '__class__':
...                     return int
...             raise AttributeError
... 
>>> C().__class__
C getattribute: __class__
<class 'int'>
>>> type(C())
<class '__main__.C'>

type() ignores the dynamically generated __class__. But isinstance does not:

>>> isinstance(C(), int)
C getattribute: __class__
True


The same applies if we use property:

>>> class D:
...     @property
...     def __class__(self):
...             return int
... 
>>> type(D())
<class '__main__.D'>
>>> isinstance(D(), int)
True


So the rules appear to be:

- you can set __class__, but only sometimes;
- type() will believe __class__, but only sometimes;
- you can generate __class__ dynamically, but not with __getattr__;
- isinstance() will believe __class__ (always?);
- and there is no indication of how much of this is deliberate language feature and how much is an accident of implementation.
History
Date User Action Args
2021-12-10 11:41:23steven.dapranosetrecipients: + steven.daprano, rhettinger, paul.moore, r.david.murray, ethan.furman, bup, Gabriele Tornetta
2021-12-10 11:41:23steven.dapranosetmessageid: <1639136483.47.0.941159479942.issue32683@roundup.psfhosted.org>
2021-12-10 11:41:23steven.dapranolinkissue32683 messages
2021-12-10 11:41:23steven.dapranocreate