[Python-ideas] Fix documentation for __instancecheck__

Joy Diamond python.gem at gmail.com
Sat Oct 27 22:21:04 EDT 2018


On Sat, Oct 27, 2018 at 10:00 PM Chris Angelico <rosuav at gmail.com> wrote:

> On Sun, Oct 28, 2018 at 12:53 PM Joy Diamond <python.gem at gmail.com> wrote:
> >> - 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?
> >
> >
> > 1.  `type(x)` gets the true actual type of `x`.
> > 2.  `x.__class__` gets the `.__class__` attribute for `x`, which by
> default gets the actual true type of `x`, but may be replace by the user to
> do other stuff.
> >
>
> Not that simple.
>
> >>> class X:
> ...   cls = "X"
> ...
> >>> class Y:
> ...   cls = "Y"
> ...
> >>> x = X()
> >>> x.__class__ = Y
> >>> x.cls
> 'Y'
> >>> type(x)
> <class '__main__.Y'>
>
> I don't know what the "true actual type" is, since I just changed it.
> In this case, type() and __class__ do exactly the same thing. The only
> way I know of (in Py3) to have them show different values is to make
> __class__ a property, and if you do that, I have no idea what uses the
> property and what uses type().
>
> Maybe this needs to be actually documented somewhere. It keeps on
> being a point of confusion.
>
> ChrisA
>

Yes, in your example, you actually changed the true actual type of `x` from
an `X` to a `Y`.

This is permitted since `X` and `Y` are "compatible".  For example, if
instead you did [making `X` and `Y` no longer compatible]:

class X(object): __slots__ = (('x',))
class Y(object): __slots__ = (('y', 'z'))
x = X()
x.__class__ = Y

You get the following:

TypeError: __class__ assignment: 'X' object layout differs from 'Y'

Thus assigning to `.__class__` (when it has not been replaced by the user),
actually transforms an instance to a new true type.

[This is actually really useful in some rare edge cases, which is why
Python supports it].

Correct, you can make `__class__` a property to replace it (or play some
really difficult games with metaclasses to support a different `__class__`).

And, yes it is a point of confusion:

1.   As per my earlier email, its best to use `.__class__`, as this is the
new way of doing things.

2.  `type(x)` was the old [Python 1] way of doing things, looks incorrectly
like a constructor.

3.  It would take weeks to fully document it; and there are many subtle
bugs probably in the python source code as to when it uses `.__class__` and
when it uses the true type -- i.e.: bypasses it all by using PY_Type(x).

CLARIFICATION: By "True actual type" -- I mean it's actual type as
implemented in the C code, and returns by the C [macro] `Py_Type(x)`.

That is, as defined at
https://github.com/python/cpython/blob/master/Include/object.h#L121

#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)

There are hundreds (704 in Python 2.7.12 for example) references to
`Py_TYPE` in the C code; thus it would take weeks to document it all.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20181027/25afea4c/attachment.html>


More information about the Python-ideas mailing list