2

I remembered learning that type is its own instance and that all new-style classes inherit from object, so I started messing around:

issubclass(type, object) # True
issubclass(object, type) # False
# issubclass(cls, cls) is always True
type(type) is type # True
type(object) is type # True

To sum that up in words, type like any new-style class is a subclass of object, and like any class, both type and object are instances of type. Here's a handy picture:

Picture of relationship between type and object in Python

Big disclaimer here that I'm not a programmer and know little about the underlying mechanics of Python, so bear with me if I sound dumb. Normally a class has to be made before its instances. However, a superclass normally has to be made before its subclass. Applying those general Python rules to type and object makes an apparent chicken-egg paradox.

So which of these classes are made first? I also suspect that because these classes are implemented in a lower-level language, they don't have to follow Python rules.

BatWannaBe
  • 4,330
  • 1
  • 14
  • 23
  • Possible duplicate of [What are metaclasses in Python?](https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python) – Klaus D. Jun 08 '19 at 01:11
  • That post has a lot of explanation of what a metaclass is (with `type` being the primordial metaclass), but I don't quite see where it answers my question of how the `type` class can inherit from its own instance `object` when Python usually enforces object creation order. – BatWannaBe Jun 08 '19 at 01:19

2 Answers2

3

From a language semantics perspective, both type and object exist, fully initialized, from the moment the program begins. Whatever the implementation does to get things into that state doesn't have to follow the rules of what the implementation allows you to do.

From a CPython implementation perspective, both type and object are statically allocated at C level, and neither is created first. You can see the variable definitions in Objects/typeobject.c. Here's object:

PyTypeObject PyBaseObject_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "object",                                   /* tp_name */
    sizeof(PyObject),                           /* tp_basicsize */
    0,                                          /* tp_itemsize */
    object_dealloc,                             /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    object_repr,                                /* tp_repr */
    ...
};

and here's type:

PyTypeObject PyType_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "type",                                     /* tp_name */
    sizeof(PyHeapTypeObject),                   /* tp_basicsize */
    sizeof(PyMemberDef),                        /* tp_itemsize */
    (destructor)type_dealloc,                   /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_reserved */
    (reprfunc)type_repr,                        /* tp_repr */
    ...
};

When interpreter initialization begins, both type and object are in a half-initialized state, and the PyType_Ready function is responsible for finishing their initialization. The type pointers are set up in the variable definitions, but setting the superclass pointers is part of PyType_Ready's job, and plenty of other initialization needs to be handled by PyType_Ready - for example, the types don't have a __dict__ yet.

Incidentally, using some weird metaclasses and the fact that Python allows you to reassign __class__ on instances of user-defined classes, we can set up our own classes A and B where B is a subclass of A and both A and B are instances of B, much like the situation with object and type. This is nothing like how object and type are actually created, though:

class DummyMeta(type):
    pass

class A(type, metaclass=DummyMeta):
    pass

class B(A):
    pass

B.__class__ = B
A.__class__ = B

print(isinstance(A, B))
print(isinstance(B, B))
print(issubclass(B, A))

Output:

True
True
True
user2357112
  • 260,549
  • 28
  • 431
  • 505
1

In Python, everything is an object:

  • 7 is an object of type int.
  • 'foo' is an object of type str.
  • None is an object of type NoneType.
  • If you define a class class Foo: pass, then Foo is an object of type class. And any object Foo() that you construct from that class is an object of type Foo.
  • If you define a function as def f(x): pass, that function itself is an object.

Every object has a type. But what is a type? That's right--even types themselves are objects with the type type.

issubclass(type, object) # True
issubclass(object, type) # False

Every class is a subclass of object, by definition. The object class is not a subclass of anything; it's the root of all "objecthood". It follows that isinstance(o, object) always returns True.

type(type) is type # True
type(object) is type # True

We know that everything in python is an instance of object. But what about the object class itself? It's an object too. What kind of object? object is a type, just like int, str, or function. Thus the type of object is type. However, any instance of the object class is not a type, but merely an object:

type(object()) is object # True

In summary, Everything is an object, even the types of other objects.

To give a non-answer the "which comes first" question, it doesn't really matter; these things are abstractions anyway and so you can make a language with whatever semantics you feel like defining, and there's nothing wrong with "cycles".

To give a more concrete answer, if you are interested in the implementation, it looks like the typedef for c-level PyObjects (which become object) in the source code for CPython here, and compare to the typedef for PyTypeObject in the same file.

Edit: The other answer better explains the CPython implementation.

Dennis
  • 2,249
  • 6
  • 12