On Sat, Oct 27, 2018 at 10:00 PM Chris Angelico <[email protected]> wrote:
> On Sun, Oct 28, 2018 at 12:53 PM Joy Diamond <[email protected]> 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.
_______________________________________________ Python-ideas mailing list [email protected] https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
