From what I've read, the built-in types such as int
, float
, dict
, etc have been implemented in C, and they used to be different from user-defined classes before Python 2.2
However, the built-in types and user defined classes have long been unified now, and the following happens
>>> int.__class__
<class 'type'>
Why does this happen? I'm aware that type
is a metaclass in Python, and all the user defined classes (by default) are (meta) instances of the same, and it gives me the impression that any object that is an instance of type
has been instantiated using the type
metaclass.
But built-in classes, such as int
, have been implemented in C, and haven't been created using the type
metaclass, right? Then, why does int.__class__
, for example, give <class 'type'>
?
Is it that, "manually", the __class__
attribute of built-in types such as int
, has been set to type
, to enable the unification?
Classes created in C are still classes and share the same core structure in memory as classes created in pure Python code. The same mechanisms used from Python to create a class can be used to create a class from C code. The only thing is that they don't "have to": a class in C can have all its inner fields hard coded, and its class members manually assigned: unlike Python code there are no guards for assigning the __class__
field of the class object.
As I said: regardless of that, these built-in classes are still "real" instances of type, as the memory layout to the special slots, both public and private have the layout used by type.
And - on top of all that - even for Python defined classes, it is possible to change the __class__
attribute of a class (but with restrictions).
yes, as simple as that: just assign a new value to the __class__
attribute of an existing instance. This will effectivelly "transform" an object into another, carrying its instance namespace. It is not the same thing as cast
does in static typed languages: with cast
in Java/C++ an object is unchanged, and the compiler just tried to apply the next operation as if the casted instance where from the cast-target class. Assigning to __class__
is a permanent change (until a reassignment, of course) to the underlying instance:
See this in action:
In [51]: class A:
...: def a(self):
...: ...
...:
In [52]: class B:
...: def b(self):
...: print("at method B")
...:
In [53]: a = A()
In [54]: a.__class__ = B
In [55]: a.b()
at method B
It won't work if both classes have diffing "memory layouts" - that is, data structures defined in native code, such as lists or tuples, or using the __slots__
mechanism. But for "ordinary" classes which keep the instance namespace in the __dict__
attribute, it just works.