Should isinstance call __getattribute__?

I'm looking for some guidance on a bug report involving isinstance and __getattribute__` please. The issue is that if your class overloads `__getattribute__`, calling isinstance on an instance will call the overloaded `__getattribute__` method when looking up `__class__`, potentially causing isinstance to fail, or return the wrong result. See b.p.o. #32683 https://bugs.python.org/issue32683 I see no reason why this isn't working as designed, __getattribute__ is intended to overload attribute access, and that could include the `__class__` attribute. Am I wrong? It has been suggested that isinstance should call `object.__getattribute__` and bypass the class' overloaded method, but I expect that would probably break objects which intentionally lie about their class. (Mocks? Stubs? Proxies?) Thanks, Steve

https://mail.python.org/pipermail/python-dev/2015-October/141953.html <https://mail.python.org/pipermail/python-dev/2015-October/141953.html> is an old thread about the difference between type(x)/Py_TYPE(x) and x.__class__ that contains some insight about this. Proxy types are one use case, although with some sharp edges. Ronald — Twitter / micro.blog: @ronaldoussoren Blog: https://blog.ronaldoussoren.net/

On Thu, Dec 09, 2021 at 05:19:00PM +0100, Ronald Oussoren wrote:
Thanks for the link Ronald, I remember that thread. It didn't really clarify things to me at the time, and re-reading it, it still doesn't.
Proxy types are one use case, although with some sharp edges.
I'm not looking for use cases. I'm looking for a better understanding of how type() and isinstance() (and presumably issubclass) work. The best I can see is that type() sometimes believes __class__ but not always, that you can sometimes change __class__ but not always, but the rules that control when and why (or why not) are not clear or documented, as far as I can see. Is there a reference for how type(obj) and isinstance(obj, T) are intended to work, or is the implementation the only reference? Thanks in advance, -- Steve

That’s because the difference between the two is not clear, and it doesn’t help that C extensions (and CPython itself) use an API that always look at the object’s type slot and not at the ``__class__`` attribute (with APIs such as ``PyObject_TypeCheck`` and ``PyDict_Check``).
I’m not sure how much of this is documented to be honest. If the documentation is lacking there’s a change for someone to dig deep and help writing the documentation ;-) Changing the type of an instance by assigning to ``__class__`` is basically allowed when the C layout for instances of the two classes are compatible, the implementation contains the details about this. IIRC this requires that both classes inherit from a shared base class and none of the intermediate classes introduce new slots (or the C equivalent of this). Ronald
— Twitter / micro.blog: @ronaldoussoren Blog: https://blog.ronaldoussoren.net/

https://mail.python.org/pipermail/python-dev/2015-October/141953.html <https://mail.python.org/pipermail/python-dev/2015-October/141953.html> is an old thread about the difference between type(x)/Py_TYPE(x) and x.__class__ that contains some insight about this. Proxy types are one use case, although with some sharp edges. Ronald — Twitter / micro.blog: @ronaldoussoren Blog: https://blog.ronaldoussoren.net/

On Thu, Dec 09, 2021 at 05:19:00PM +0100, Ronald Oussoren wrote:
Thanks for the link Ronald, I remember that thread. It didn't really clarify things to me at the time, and re-reading it, it still doesn't.
Proxy types are one use case, although with some sharp edges.
I'm not looking for use cases. I'm looking for a better understanding of how type() and isinstance() (and presumably issubclass) work. The best I can see is that type() sometimes believes __class__ but not always, that you can sometimes change __class__ but not always, but the rules that control when and why (or why not) are not clear or documented, as far as I can see. Is there a reference for how type(obj) and isinstance(obj, T) are intended to work, or is the implementation the only reference? Thanks in advance, -- Steve

That’s because the difference between the two is not clear, and it doesn’t help that C extensions (and CPython itself) use an API that always look at the object’s type slot and not at the ``__class__`` attribute (with APIs such as ``PyObject_TypeCheck`` and ``PyDict_Check``).
I’m not sure how much of this is documented to be honest. If the documentation is lacking there’s a change for someone to dig deep and help writing the documentation ;-) Changing the type of an instance by assigning to ``__class__`` is basically allowed when the C layout for instances of the two classes are compatible, the implementation contains the details about this. IIRC this requires that both classes inherit from a shared base class and none of the intermediate classes introduce new slots (or the C equivalent of this). Ronald
— Twitter / micro.blog: @ronaldoussoren Blog: https://blog.ronaldoussoren.net/
participants (2)
-
Ronald Oussoren
-
Steven D'Aprano