On Sat, Oct 27, 2018 at 10:00 PM Chris Angelico
On Sun, Oct 28, 2018 at 12:53 PM Joy Diamond
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)
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.