<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Metaclass on Opensource internals</title><link>https://articles.9j.nz/tags/metaclass/</link><description>Posts filed under Metaclass on Opensource internals.</description><generator>Hugo</generator><language>en-US</language><managingEditor>Jay Hubbert</managingEditor><lastBuildDate>Tue, 20 Oct 2026 09:00:00 +0000</lastBuildDate><atom:link href="https://articles.9j.nz/tags/metaclass/index.xml" rel="self" type="application/rss+xml"/><item><title>CPython's type Implementation in C (the Metaclass)</title><link>https://articles.9j.nz/posts/how-cpython-type-is-implemented/</link><pubDate>Tue, 20 Oct 2026 09:00:00 +0000</pubDate><dc:creator>Jay Hubbert</dc:creator><guid isPermaLink="true">https://articles.9j.nz/posts/how-cpython-type-is-implemented/</guid><category>python</category><category>cpython</category><category>internals</category><category>type</category><category>metaclass</category><description>The PyTypeObject struct, method slots, tp_dict, MRO computation, heap vs static types, and type creation traced through the CPython 3.15 source.</description><content:encoded>&lt;p&gt;&lt;code&gt;type&lt;/code&gt; is the metaclass of every Python object: calling &lt;code&gt;type(obj)&lt;/code&gt; returns the&#10;class of &lt;code&gt;obj&lt;/code&gt;, and &lt;code&gt;class Foo:&lt;/code&gt; ultimately calls &lt;code&gt;type.__new__&lt;/code&gt;. In CPython&#10;the same object that represents &lt;code&gt;type&lt;/code&gt; in Python is &lt;code&gt;PyType_Type&lt;/code&gt;, and every&#10;class is a &lt;code&gt;PyTypeObject&lt;/code&gt; (or, for classes created at runtime, a&#10;&lt;code&gt;PyHeapTypeObject&lt;/code&gt;). This article traces how that machinery is implemented in&#10;&lt;code&gt;Objects/typeobject.c&lt;/code&gt; and &lt;code&gt;Include/cpython/object.h&lt;/code&gt; in CPython 3.15.&lt;/p&gt;&#10;&lt;h2 id="the-object-struct"&gt;The object struct&lt;/h2&gt;&#10;&lt;p&gt;Every Python object begins with &lt;code&gt;PyObject_VAR_HEAD&lt;/code&gt;. A type object adds dozens&#10;of slots that describe how instances behave, plus the things that make a type a&#10;first-class object: its &lt;code&gt;tp_dict&lt;/code&gt;, bases, and MRO. The 3.15 layout is:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Include/cpython/object.h&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;_typeobject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject_VAR_HEAD&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;tp_basicsize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tp_itemsize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;destructor&lt;/span&gt; &lt;span class="n"&gt;tp_dealloc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;tp_vectorcall_offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;getattrfunc&lt;/span&gt; &lt;span class="n"&gt;tp_getattr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;setattrfunc&lt;/span&gt; &lt;span class="n"&gt;tp_setattr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyAsyncMethods&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_as_async&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reprfunc&lt;/span&gt; &lt;span class="n"&gt;tp_repr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyNumberMethods&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_as_number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PySequenceMethods&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_as_sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyMappingMethods&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_as_mapping&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hashfunc&lt;/span&gt; &lt;span class="n"&gt;tp_hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ternaryfunc&lt;/span&gt; &lt;span class="n"&gt;tp_call&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reprfunc&lt;/span&gt; &lt;span class="n"&gt;tp_str&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;getattrofunc&lt;/span&gt; &lt;span class="n"&gt;tp_getattro&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;setattrofunc&lt;/span&gt; &lt;span class="n"&gt;tp_setattro&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyBufferProcs&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_as_buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;tp_flags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_doc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;traverseproc&lt;/span&gt; &lt;span class="n"&gt;tp_traverse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inquiry&lt;/span&gt; &lt;span class="n"&gt;tp_clear&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;richcmpfunc&lt;/span&gt; &lt;span class="n"&gt;tp_richcompare&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;tp_weaklistoffset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;getiterfunc&lt;/span&gt; &lt;span class="n"&gt;tp_iter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;iternextfunc&lt;/span&gt; &lt;span class="n"&gt;tp_iternext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyMethodDef&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_methods&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyMemberDef&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_members&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyGetSetDef&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_getset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* borrowed on static, strong on heap */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_dict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* the class __dict__ */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;descrgetfunc&lt;/span&gt; &lt;span class="n"&gt;tp_descr_get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;descrsetfunc&lt;/span&gt; &lt;span class="n"&gt;tp_descr_set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;tp_dictoffset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;initproc&lt;/span&gt; &lt;span class="n"&gt;tp_init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;allocfunc&lt;/span&gt; &lt;span class="n"&gt;tp_alloc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;newfunc&lt;/span&gt; &lt;span class="n"&gt;tp_new&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;freefunc&lt;/span&gt; &lt;span class="n"&gt;tp_free&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;inquiry&lt;/span&gt; &lt;span class="n"&gt;tp_is_gc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_bases&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* tuple of direct bases */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_mro&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* method resolution order tuple */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* unused */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_subclasses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* dict, or index for static builtins */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tp_weaklist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;destructor&lt;/span&gt; &lt;span class="n"&gt;tp_del&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tp_version_tag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;destructor&lt;/span&gt; &lt;span class="n"&gt;tp_finalize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vectorcallfunc&lt;/span&gt; &lt;span class="n"&gt;tp_vectorcall&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;tp_watched&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;tp_versions_used&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_Py_iteritemfunc&lt;/span&gt; &lt;span class="n"&gt;_tp_iteritem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;The slot fields (&lt;code&gt;tp_methods&lt;/code&gt;, &lt;code&gt;tp_members&lt;/code&gt;, &lt;code&gt;tp_getset&lt;/code&gt;) are arrays that&#10;&lt;code&gt;PyType_Ready&lt;/code&gt; turns into real descriptors in &lt;code&gt;tp_dict&lt;/code&gt;. &lt;code&gt;tp_as_number&lt;/code&gt; and&#10;friends are method suites: pointers to structs of function pointers that&#10;implement &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;len()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, etc. They are populated either statically (for&#10;built-in types) or by &lt;code&gt;add_operators()&lt;/code&gt; during readying.&lt;/p&gt;&#10;&lt;h2 id="the-real-layout-pyheaptypeobject"&gt;The real layout: &lt;code&gt;PyHeapTypeObject&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;A type created at runtime (&lt;code&gt;class Foo:&lt;/code&gt;) is not just a &lt;code&gt;PyTypeObject&lt;/code&gt;. It is a&#10;&lt;code&gt;PyHeapTypeObject&lt;/code&gt;, whose first member is the &lt;code&gt;PyTypeObject&lt;/code&gt; (so it can be&#10;cast back and forth), followed by the method-suite structs and heap-only&#10;metadata:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Include/cpython/object.h&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;_heaptypeobject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="n"&gt;ht_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyAsyncMethods&lt;/span&gt; &lt;span class="n"&gt;as_async&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyNumberMethods&lt;/span&gt; &lt;span class="n"&gt;as_number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyMappingMethods&lt;/span&gt; &lt;span class="n"&gt;as_mapping&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PySequenceMethods&lt;/span&gt; &lt;span class="n"&gt;as_sequence&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyBufferProcs&lt;/span&gt; &lt;span class="n"&gt;as_buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_slots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_qualname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;_dictkeysobject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_cached_keys&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_ht_tpname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ht_token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;_specialization_cache&lt;/span&gt; &lt;span class="n"&gt;_spec_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#ifdef Py_GIL_DISABLED&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;unique_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#endif&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;PyHeapTypeObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;The distinction is exposed by the &lt;code&gt;Py_TPFLAGS_HEAPTYPE&lt;/code&gt; flag. Static types&#10;(&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &amp;hellip;) are immortal, singletons stored in the binary;&#10;heap types are allocated with &lt;code&gt;PyObject_GC_New&lt;/code&gt; and reference-counted.&lt;/p&gt;&#10;&lt;h2 id="tp_dict-the-class-dictionary"&gt;&lt;code&gt;tp_dict&lt;/code&gt;: the class dictionary&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;tp_dict&lt;/code&gt; is the &lt;code&gt;__dict__&lt;/code&gt; of the class. For heap types it is a real&#10;&lt;code&gt;dict&lt;/code&gt; created during type creation. For static built-in types CPython stores&#10;&lt;code&gt;tp_dict&lt;/code&gt; per-interpreter in &lt;code&gt;managed_static_type_state&lt;/code&gt; (so each interpreter&#10;gets its own dict), which is why &lt;code&gt;lookup_tp_dict&lt;/code&gt; branches on&#10;&lt;code&gt;_Py_TPFLAGS_STATIC_BUILTIN&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kr"&gt;inline&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;lookup_tp_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_Py_TPFLAGS_STATIC_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyInterpreterState&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;interp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_PyInterpreterState_GET&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;managed_static_type_state&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_PyStaticType_GetState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_dict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_dict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Attribute lookup (&lt;code&gt;type.__getattribute__&lt;/code&gt;, i.e. &lt;code&gt;_PyType_Lookup&lt;/code&gt;) searches&#10;&lt;code&gt;tp_dict&lt;/code&gt; of the type and then walks &lt;code&gt;tp_mro&lt;/code&gt;, reading each base&amp;rsquo;s &lt;code&gt;tp_dict&lt;/code&gt;.&#10;When a class is modified, &lt;code&gt;PyType_Modified&lt;/code&gt; invalidates the version tag and&#10;notifies watchers.&lt;/p&gt;&#10;&lt;h2 id="mro"&gt;MRO&lt;/h2&gt;&#10;&lt;p&gt;The MRO is stored as a tuple in &lt;code&gt;tp_mro&lt;/code&gt;, with the type itself first and&#10;&lt;code&gt;object&lt;/code&gt; last. It is computed by &lt;code&gt;mro_internal&lt;/code&gt;, which calls the type&amp;rsquo;s (or&#10;metaclass&amp;rsquo;s) &lt;code&gt;mro()&lt;/code&gt; and falls back to C3 linearization:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;mro_internal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;p_old_mro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;old_mro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Py_XNewRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lookup_tp_mro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;new_mro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mro_invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* may re-enter */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lookup_tp_mro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;old_mro&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;set_tp_mro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_mro&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;type_mro_modified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_mro&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;mro_internal&lt;/code&gt; also records the old MRO so that, when bases change, subclasses&#10;can be updated. &lt;code&gt;type_mro_modified&lt;/code&gt; checks whether the type is eligible for the&#10;attribute cache (version tag): a custom &lt;code&gt;mro()&lt;/code&gt; or a non-subtype base clears&#10;&lt;code&gt;tp_versions_used&lt;/code&gt; to &lt;code&gt;_Py_ATTR_CACHE_UNUSED&lt;/code&gt;, disabling caching for that&#10;type and its subclasses.&lt;/p&gt;&#10;&lt;h2 id="type-creation"&gt;Type creation&lt;/h2&gt;&#10;&lt;p&gt;Creating a class (&lt;code&gt;class Foo(Bar): ...&lt;/code&gt;) calls &lt;code&gt;type.__new__&lt;/code&gt;, which in C is&#10;&lt;code&gt;type_new&lt;/code&gt; -&amp;gt; ultimately &lt;code&gt;type_from_slots_or_spec&lt;/code&gt; for the C API path. The&#10;public entry points are:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;PyType_FromMetaclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;metaclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyType_Spec&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;PyType_FromSpecWithBases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyType_Spec&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bases&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;PyType_FromSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyType_Spec&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;A &lt;code&gt;PyType_Spec&lt;/code&gt; describes name, basicsize, itemsize, flags and a list of&#10;&lt;code&gt;PyType_Slot&lt;/code&gt; entries mapping slot IDs (&lt;code&gt;Py_tp_init&lt;/code&gt;, &lt;code&gt;Py_tp_methods&lt;/code&gt;, &amp;hellip;)&#10;to function pointers. &lt;code&gt;type_from_slots_or_spec&lt;/code&gt; allocates a&#10;&lt;code&gt;PyHeapTypeObject&lt;/code&gt;, copies the spec into the slots, sets &lt;code&gt;tp_base&lt;/code&gt;/&lt;code&gt;tp_bases&lt;/code&gt;,&#10;creates &lt;code&gt;tp_dict&lt;/code&gt;, and finally calls &lt;code&gt;PyType_Ready&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;PyType_Ready&lt;/code&gt; is the choke point that finishes initialization:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;PyType_Ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Py_TPFLAGS_READY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_Py_TPFLAGS_STATIC_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Py_TPFLAGS_HEAPTYPE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;type_add_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Py_TPFLAGS_IMMUTABLETYPE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;_Py_SetImmortalUntracked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;BEGIN_TYPE_LOCK&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_flags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Py_TPFLAGS_READY&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;type_ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;END_TYPE_LOCK&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;type_ready&lt;/code&gt; (called under the type lock) fills in inherited slots from the&#10;base, calls &lt;code&gt;add_operators&lt;/code&gt; to turn &lt;code&gt;tp_methods&lt;/code&gt;/&lt;code&gt;tp_members&lt;/code&gt;/&lt;code&gt;tp_getset&lt;/code&gt;&#10;into descriptors in &lt;code&gt;tp_dict&lt;/code&gt;, computes &lt;code&gt;tp_mro&lt;/code&gt; via &lt;code&gt;mro_internal&lt;/code&gt;, sets&#10;&lt;code&gt;tp_flags |= Py_TPFLAGS_READY&lt;/code&gt;, and assigns a version tag. After this the type&#10;is usable.&lt;/p&gt;&#10;&lt;h2 id="heap-vs-static-types"&gt;Heap vs static types&lt;/h2&gt;&#10;&lt;p&gt;Static types (&lt;code&gt;PyLong_Type&lt;/code&gt;, &lt;code&gt;PyList_Type&lt;/code&gt;, &amp;hellip;) are compile-time&#10;&lt;code&gt;PyTypeObject&lt;/code&gt; constants. They carry &lt;code&gt;_Py_TPFLAGS_STATIC_BUILTIN&lt;/code&gt; (for the&#10;core builtins) or are plain static types in extension modules. They are&#10;immortal and, for builtins, have per-interpreter &lt;code&gt;tp_dict&lt;/code&gt;/&lt;code&gt;tp_subclasses&lt;/code&gt;/&#10;&lt;code&gt;tp_weaklist&lt;/code&gt; stored in &lt;code&gt;managed_static_type_state&lt;/code&gt;. &lt;code&gt;init_static_type&lt;/code&gt;&#10;registers this state and calls &lt;code&gt;type_ready&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;init_static_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyInterpreterState&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;interp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;isbuiltin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;type_add_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_Py_TPFLAGS_STATIC_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;type_add_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Py_TPFLAGS_IMMUTABLETYPE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;managed_static_type_state_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isbuiltin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;type_ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;Heap types differ in several ways:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Allocated on the heap and reference-counted (&lt;code&gt;Py_TPFLAGS_HAVE_GC&lt;/code&gt; usually).&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Py_TPFLAGS_HEAPTYPE&lt;/code&gt; is set; &lt;code&gt;tp_name&lt;/code&gt;/&lt;code&gt;ht_name&lt;/code&gt; live in the&#10;&lt;code&gt;PyHeapTypeObject&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Mutable by default: attributes can be added, &lt;code&gt;__bases__&lt;/code&gt; can change, MRO&#10;recomputed.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;tp_base&lt;/code&gt; is a strong reference; for static types it is a borrowed&#10;reference.&lt;/li&gt;&#10;&lt;li&gt;Version tags come from a per-interpreter counter (&lt;code&gt;NEXT_VERSION_TAG&lt;/code&gt;)&#10;rather than the global counter used for static types.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The &lt;code&gt;Py_TPFLAGS_IMMUTABLETYPE&lt;/code&gt; flag (set by &lt;code&gt;PyType_Ready&lt;/code&gt; for any non-heap&#10;type, and available to heap types via &lt;code&gt;Py_TPFLAGS_IMMUTABLETYPE&lt;/code&gt;) controls&#10;whether &lt;code&gt;__setattr__&lt;/code&gt;/&lt;code&gt;__delattr__&lt;/code&gt; on the type itself are allowed, which is&#10;what makes a class &amp;ldquo;frozen&amp;rdquo;.&lt;/p&gt;&#10;&lt;h2 id="type-lookup-and-the-version-tag"&gt;Type lookup and the version tag&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;_PyType_Lookup&lt;/code&gt; walks the MRO and reads &lt;code&gt;tp_dict&lt;/code&gt;. To make attribute access&#10;fast, CPython caches results keyed by &lt;code&gt;tp_version_tag&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c (conceptually)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;_PyType_Lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* version tag fast path */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;assign_version_tag&lt;/code&gt; (called lazily) assigns a tag to a type only after all&#10;its bases have tags, so a cached lookup is valid for the whole hierarchy.&#10;&lt;code&gt;PyType_Modified&lt;/code&gt; clears the tag (setting it to 0) and recursively clears&#10;subclasses, forcing re-lookup. This is why mutating a base class invalidates&#10;caches everywhere below it.&lt;/p&gt;&#10;&lt;h2 id="type__call__-and-instance-creation"&gt;&lt;code&gt;type.__call__&lt;/code&gt; and instance creation&lt;/h2&gt;&#10;&lt;p&gt;When you write &lt;code&gt;Foo(x)&lt;/code&gt;, Python calls &lt;code&gt;type.__call__(Foo, x)&lt;/code&gt;. In C that is&#10;&lt;code&gt;type_call&lt;/code&gt;, which runs &lt;code&gt;tp_new&lt;/code&gt; to allocate and initialize the instance, then&#10;&lt;code&gt;tp_init&lt;/code&gt; to initialize it, and finally returns the object (or propagates an&#10;error). The slots are inherited from the base during &lt;code&gt;type_ready&lt;/code&gt;, so a type&#10;that does not define &lt;code&gt;tp_new&lt;/code&gt; gets &lt;code&gt;object.__new__&lt;/code&gt; (i.e. &lt;code&gt;slot_tp_new&lt;/code&gt;):&lt;/p&gt;&#10;&lt;div class="code-block"&gt;&#10; &lt;div class="code-block-head"&gt;&#10; &lt;span class="code-lang"&gt;c&lt;/span&gt;&#10; &lt;button class="code-copy" type="button" data-copy&gt;Copy&lt;/button&gt;&#10; &lt;/div&gt;&#10; &lt;div class="code-block-body"&gt;&#10; &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Objects/typeobject.c&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;type_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tp_new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tp_init&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;tp_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;Py_DECREF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;tp_alloc&lt;/code&gt; (default &lt;code&gt;PyType_GenericAlloc&lt;/code&gt;) allocates the instance using&#10;&lt;code&gt;tp_basicsize&lt;/code&gt;/&lt;code&gt;tp_itemsize&lt;/code&gt; and sets &lt;code&gt;ob_type&lt;/code&gt;. &lt;code&gt;tp_free&lt;/code&gt; is the matching&#10;deallocator. For GC-tracked types &lt;code&gt;tp_alloc&lt;/code&gt; calls &lt;code&gt;PyObject_GC_New&lt;/code&gt; and&#10;&lt;code&gt;tp_free&lt;/code&gt; calls &lt;code&gt;PyObject_GC_Del&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="slots-and-inheritance"&gt;Slots and inheritance&lt;/h2&gt;&#10;&lt;p&gt;Slots are not inherited field-by-field naively. &lt;code&gt;type_ready&lt;/code&gt; calls&#10;&lt;code&gt;inherit_slots&lt;/code&gt; (and &lt;code&gt;add_operators&lt;/code&gt;), which copies function pointers from&#10;the base when the derived type leaves a slot NULL, and merges the method suites&#10;(&lt;code&gt;tp_as_number&lt;/code&gt;, etc.) by copying individual function pointers. This is why a&#10;subclass of &lt;code&gt;int&lt;/code&gt; automatically gets &lt;code&gt;nb_add&lt;/code&gt; unless it overrides &lt;code&gt;__add__&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;The &lt;code&gt;tp_methods&lt;/code&gt;/&lt;code&gt;tp_members&lt;/code&gt;/&lt;code&gt;tp_getset&lt;/code&gt; arrays are processed by&#10;&lt;code&gt;add_methods&lt;/code&gt;/&lt;code&gt;add_members&lt;/code&gt;/&lt;code&gt;add_getset&lt;/code&gt;, which build &lt;code&gt;PyCFunction&lt;/code&gt;/member/&#10;getset descriptors and insert them into &lt;code&gt;tp_dict&lt;/code&gt;. After that the arrays are no&#10;longer needed for lookup (the dict holds the descriptors).&lt;/p&gt;&#10;&lt;h2 id="subclassing-rules"&gt;Subclassing rules&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;PyType_Ready&lt;/code&gt; and &lt;code&gt;type_new&lt;/code&gt; enforce that a base type must have&#10;&lt;code&gt;Py_TPFLAGS_BASETYPE&lt;/code&gt; set; otherwise &lt;code&gt;type(...)&lt;/code&gt; raises &amp;ldquo;is not an acceptable&#10;base type&amp;rdquo;. &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt; and most builtins set this flag. Some types (e.g.&#10;&lt;code&gt;list&lt;/code&gt; historically, though now subclassable) clear it to forbid subclassing in&#10;C.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;type_is_subtype&lt;/code&gt; and &lt;code&gt;is_subtype_with_mro&lt;/code&gt; are used to validate MROs. A type&#10;cannot be its own base, and the MRO must be consistent (C3 merge). If no&#10;linearization exists, &lt;code&gt;type()&lt;/code&gt; raises &lt;code&gt;TypeError: Cannot create a consistent method resolution order&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="type_new-in-detail"&gt;&lt;code&gt;type_new&lt;/code&gt; in detail&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;type_new&lt;/code&gt; (the C implementation of &lt;code&gt;type.__new__&lt;/code&gt;) does the heavy lifting for&#10;&lt;code&gt;class&lt;/code&gt; statements:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Validate &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;bases&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Choose the metaclass: if &lt;code&gt;bases&lt;/code&gt; is non-empty, use the most derived&#10;metaclass among the bases&amp;rsquo; metaclasses; otherwise use the provided&#10;metaclass or &lt;code&gt;type&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Allocate a &lt;code&gt;PyHeapTypeObject&lt;/code&gt; sized for &lt;code&gt;tp_basicsize + nslots * sizeof(void*)&lt;/code&gt;&#10;when &lt;code&gt;__slots__&lt;/code&gt; is present.&lt;/li&gt;&#10;&lt;li&gt;Copy &lt;code&gt;tp_name&lt;/code&gt; (stored in &lt;code&gt;_ht_tpname&lt;/code&gt;), &lt;code&gt;ht_name&lt;/code&gt;, &lt;code&gt;ht_qualname&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;Set &lt;code&gt;tp_base&lt;/code&gt; to the best base (or &lt;code&gt;object&lt;/code&gt;), &lt;code&gt;tp_bases&lt;/code&gt; to the tuple.&lt;/li&gt;&#10;&lt;li&gt;Call &lt;code&gt;PyType_Ready&lt;/code&gt;.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;For &lt;code&gt;__slots__&lt;/code&gt;, &lt;code&gt;type_new&lt;/code&gt; creates a &lt;code&gt;member_descriptor&lt;/code&gt; for each slot and&#10;stores the slot index in the instance dict layout (&lt;code&gt;tp_dictoffset&lt;/code&gt;,&#10;&lt;code&gt;tp_weaklistoffset&lt;/code&gt;).&lt;/p&gt;&#10;&lt;h2 id="managed-dicts-and-__dict__"&gt;Managed dicts and &lt;code&gt;__dict__&lt;/code&gt;&lt;/h2&gt;&#10;&lt;p&gt;Heap types may have a managed &lt;code&gt;__dict__&lt;/code&gt; (&lt;code&gt;Py_TPFLAGS_MANAGED_DICT&lt;/code&gt;) instead&#10;of a fixed &lt;code&gt;tp_dictoffset&lt;/code&gt;. In 3.15 managed dicts are the default for heap&#10;types, storing the instance dict in a side table indexed by the object. This&#10;saves memory when most instances have no extra attributes. &lt;code&gt;tp_dictoffset&lt;/code&gt;&#10;is set to 0 in that case and &lt;code&gt;PyObject_GenericGetAttr&lt;/code&gt; uses&#10;&lt;code&gt;_PyObject_GetDictPtr&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="garbage-collection"&gt;Garbage collection&lt;/h2&gt;&#10;&lt;p&gt;Heap types set &lt;code&gt;Py_TPFLAGS_HAVE_GC&lt;/code&gt; and provide &lt;code&gt;tp_traverse&lt;/code&gt;/&lt;code&gt;tp_clear&lt;/code&gt;&#10;(generated by the GC machinery for the type&amp;rsquo;s slots and &lt;code&gt;tp_dict&lt;/code&gt;). Static types&#10;that contain references also set these flags. &lt;code&gt;tp_is_gc&lt;/code&gt; answers whether a&#10;particular instance is tracked. &lt;code&gt;_PyObject_GC_TRACK&lt;/code&gt; is called in &lt;code&gt;tp_alloc&lt;/code&gt;&#10;for GC types.&lt;/p&gt;&#10;&lt;h2 id="free-threading-notes"&gt;Free-threading notes&lt;/h2&gt;&#10;&lt;p&gt;In &lt;code&gt;Py_GIL_DISABLED&lt;/code&gt; builds, several fields become per-interpreter or&#10;atomically accessed: &lt;code&gt;tp_dict&lt;/code&gt;, &lt;code&gt;tp_mro&lt;/code&gt;, &lt;code&gt;tp_bases&lt;/code&gt; for static builtins live&#10;in &lt;code&gt;managed_static_type_state&lt;/code&gt;; &lt;code&gt;tp_version_tag&lt;/code&gt; uses atomic loads/stores;&#10;&lt;code&gt;tp_subclasses&lt;/code&gt; is per-interpreter. Type mutation takes the type lock&#10;(&lt;code&gt;TYPE_LOCK&lt;/code&gt;) and may stop the world. New types are marked &amp;ldquo;revealed&amp;rdquo; via&#10;&lt;code&gt;_Py_TYPE_REVEALED_FLAG&lt;/code&gt; only after they are fully initialized, so other&#10;threads never observe a half-ready type.&lt;/p&gt;&#10;&lt;h2 id="type-itself"&gt;&lt;code&gt;type&lt;/code&gt; itself&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;PyType_Type&lt;/code&gt; is defined statically in &lt;code&gt;Objects/typeobject.c&lt;/code&gt; with&#10;&lt;code&gt;tp_name = &amp;quot;type&amp;quot;&lt;/code&gt;, &lt;code&gt;tp_basicsize = sizeof(PyHeapTypeObject)&lt;/code&gt;,&#10;&lt;code&gt;tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE&lt;/code&gt;,&#10;and slots pointing at &lt;code&gt;type_*&lt;/code&gt; functions (&lt;code&gt;type_repr&lt;/code&gt;, &lt;code&gt;type_call&lt;/code&gt;,&#10;&lt;code&gt;type_getattro&lt;/code&gt;, etc.). Its metaclass is itself (&lt;code&gt;PyType_Type.tp_type = &amp;amp;PyType_Type&lt;/code&gt;),&#10;which is the fixed point that makes &lt;code&gt;type(type) is type&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="key-takeaways"&gt;Key takeaways&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;type&lt;/code&gt; is &lt;code&gt;PyType_Type&lt;/code&gt;; every class is a &lt;code&gt;PyTypeObject&lt;/code&gt;, and runtime&#10;classes are &lt;code&gt;PyHeapTypeObject&lt;/code&gt;s (heap-allocated, GC-tracked).&lt;/li&gt;&#10;&lt;li&gt;Slots (&lt;code&gt;tp_*&lt;/code&gt;) describe instance behavior; method suites (&lt;code&gt;tp_as_number&lt;/code&gt;,&#10;&amp;hellip;) group operator implementations.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;tp_dict&lt;/code&gt; is the class &lt;code&gt;__dict__&lt;/code&gt;; static builtins store theirs&#10;per-interpreter.&lt;/li&gt;&#10;&lt;li&gt;The MRO is a tuple in &lt;code&gt;tp_mro&lt;/code&gt;, computed by &lt;code&gt;mro_internal&lt;/code&gt; (C3 by&#10;default, custom &lt;code&gt;mro()&lt;/code&gt; allowed).&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;PyType_Ready&lt;/code&gt; finishes a type: inheritance, operators, MRO, flags, version&#10;tag.&lt;/li&gt;&#10;&lt;li&gt;Static types are immortal and immutable by default; heap types are mutable&#10;and reference-counted.&lt;/li&gt;&#10;&lt;li&gt;Version tags power the attribute-lookup cache; &lt;code&gt;PyType_Modified&lt;/code&gt; invalidates&#10;them.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="type-watchers-and-the-attribute-cache"&gt;Type watchers and the attribute cache&lt;/h2&gt;&#10;&lt;p&gt;CPython 3.11+ added &lt;code&gt;PyType_AddWatcher&lt;/code&gt;/&lt;code&gt;PyType_Watch&lt;/code&gt; so that tools&#10;(jit/optimizer, debuggers) can be notified when a type changes. A watcher bit&#10;is stored in &lt;code&gt;tp_watched&lt;/code&gt;; when &lt;code&gt;PyType_Modified&lt;/code&gt; runs it invokes every&#10;registered callback for that type. &lt;code&gt;PyType_Modified&lt;/code&gt; also clears the version&#10;tag and walks subclasses, so watchers see a consistent invalidation.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;tp_cache&lt;/code&gt; is a legacy field (always NULL now); caching moved to the&#10;per-interpreter &lt;code&gt;type_cache&lt;/code&gt; hash table keyed by &lt;code&gt;(version, name)&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="further-reading"&gt;Further reading&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;Objects/typeobject.c&lt;/code&gt; &amp;ndash; all of the above.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Include/cpython/object.h&lt;/code&gt; &amp;ndash; &lt;code&gt;PyTypeObject&lt;/code&gt; and &lt;code&gt;PyHeapTypeObject&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Include/cpython/object.h&lt;/code&gt; (type slots) and &lt;code&gt;Include/pytypedefs.h&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;Objects/typeobject.c&lt;/code&gt; (&lt;code&gt;mro_internal&lt;/code&gt;, &lt;code&gt;type_ready&lt;/code&gt;, &lt;code&gt;type_from_slots_or_spec&lt;/code&gt;).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item></channel></rss>