<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jason Madden</title><link>https://seecoresoftware.com/</link><description>Stuff I write about.</description><atom:link href="https://seecoresoftware.com/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2021 &lt;a href="mailto:jason@seecoresoftware.com"&gt;Jason Madden&lt;/a&gt; </copyright><lastBuildDate>Sat, 04 Dec 2021 18:43:38 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Transaction IDs in ZODB and RelStorage</title><link>https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;figure&gt;&lt;img src="https://seecoresoftware.com/blog/2021/05/fingerprint-5661868_1280.png"&gt;&lt;/figure&gt; &lt;div&gt;&lt;p&gt;Earlier, &lt;a class="reference external" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html"&gt;we introduced ZODB&lt;/a&gt; as a
transactional object database. This post takes a look into an
implementation detail: transaction IDs. What are they, what are they
used for, and why does any of this matter?&lt;/p&gt;
&lt;p&gt;Along the way, we'll also look at another type of identifier used in
ZODB, object IDs.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#implementing-zodb-motivation-for-transaction-ids" id="id1"&gt;Implementing ZODB: Motivation for Transaction IDs&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#object-ids" id="id2"&gt;Object IDs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#storage-details-and-referencing-objects" id="id3"&gt;Storage Details and Referencing Objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#conflict-detection-through-serial-numbers" id="id4"&gt;Conflict Detection Through Serial Numbers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#setting-the-serial-number" id="id5"&gt;Setting the Serial Number&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#properties-of-transaction-ids" id="id6"&gt;Properties of Transaction IDs&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#transaction-ids-are-based-on-the-current-time" id="id7"&gt;Transaction IDs Are Based On the Current Time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#transaction-ids-must-increase" id="id8"&gt;Transaction IDs Must Increase&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#different-transaction-ids-can-have-the-same-timestamp" id="id9"&gt;Different Transaction IDs Can Have the Same Timestamp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#uses-of-transaction-ids" id="id10"&gt;Uses of Transaction IDs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#transaction-ids-in-relstorage" id="id11"&gt;Transaction IDs in RelStorage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Rather than try to talk about &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Database_transaction"&gt;transactions&lt;/a&gt; in general, I'll use
ZODB-colored lenses and only talk about transactions and transaction
IDs as they relate to ZODB and how ZODB uses them.&lt;/p&gt;
&lt;div class="section" id="implementing-zodb-motivation-for-transaction-ids"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id1"&gt;Implementing ZODB: Motivation for Transaction IDs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To understand how ZODB uses transaction IDs, let's work through an
example of a basic use of ZODB, pausing along the way to consider how
ZODB is actually implementing things.&lt;/p&gt;
&lt;p&gt;We know that ZODB stores persistent objects; we can ask ZODB to fetch
a persistent object for us (perhaps much later, or perhaps from a
different machine) and it will come back to us in the state we left
it. The easiest way to do this is by using normal Python mechanisms to
build and walk through an object graph, starting from the ZODB root
object.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ZODB.DB&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ZODB.DemoStorage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DemoStorage&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;persistent&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Persistent&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;transaction&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DemoStorage&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;transaction._transaction.Transaction object at 0x110604c80&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6101da5230944d61b6e2071b6ec2f137-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We can use a different connection (simulating a different machine,
say) and walk through the object graph, starting from the root, to
find our object.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_627c132740544b72ae82a315e9bd7d1e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_627c132740544b72ae82a315e9bd7d1e-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;transaction._transaction.Transaction object at 0x110a3ff00&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_627c132740544b72ae82a315e9bd7d1e-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_627c132740544b72ae82a315e9bd7d1e-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a name="rest_code_627c132740544b72ae82a315e9bd7d1e-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;persistent.Persistent object at 0x110a973d0 oid 0x3e2ee68c1ab9f8d3 in &amp;lt;Connection at 110a3fa00&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;div class="section" id="object-ids"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id2"&gt;Object IDs&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Everything starts from the root. But where does the root object come from? Let's look at &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/Connection.py#L337"&gt;the root()
method of the Connection&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_5abfa2bac9214739b6450aeeb2bc79b5-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_5abfa2bac9214739b6450aeeb2bc79b5-2"&gt;&lt;/a&gt;    &lt;span class="sd"&gt;"""Return the database root object."""&lt;/span&gt;
&lt;a name="rest_code_5abfa2bac9214739b6450aeeb2bc79b5-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RootConvenience&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;That points us to &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/Connection.py#L221"&gt;the get()  method&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-2"&gt;&lt;/a&gt;    &lt;span class="sd"&gt;"""Return the persistent object with oid 'oid'."""&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-3"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# Simplified,  details removed&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-7"&gt;&lt;/a&gt;    &lt;span class="n"&gt;pickle_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-8"&gt;&lt;/a&gt;    &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getGhost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pickle_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_a54eadede799422bb6896e70e61e47e9-9"&gt;&lt;/a&gt;    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_ghost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&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;a name="rest_code_a54eadede799422bb6896e70e61e47e9-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;There's a lot going on in these two little snippets, so lets unpack
them, starting with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;get().&lt;/span&gt;&lt;/tt&gt; What can we deduce from this?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A Connection caches objects, and when objects can't be found in the
cache, it asks the "storage" object to load it. (More on the storage
in a minute.)&lt;/li&gt;
&lt;li&gt;Persistent objects are identified by "OIDs" (short for "object
identifier", or just "object ID"). Just as any Python object in
memory has &lt;a class="reference external" href="https://docs.python.org/3/library/functions.html#id"&gt;a unique identifier&lt;/a&gt;—returned by &lt;tt class="docutils literal"&gt;id(obj)&lt;/tt&gt; and commonly seen in &lt;tt class="docutils literal"&gt;repr&lt;/tt&gt; output as a
hexadecimal number, as in &lt;tt class="docutils literal"&gt;&amp;lt;persistent.Persistent object at
&lt;span class="pre"&gt;0x110a973d0...&amp;gt;&lt;/span&gt;&lt;/tt&gt;—, any persistent object stored in the ZODB
has a unique identifier, its OID. An important difference is that &lt;tt class="docutils literal"&gt;id(obj)&lt;/tt&gt;
is only meaningful within the scope of a single process, but the ZODB
OID is usable from any process connected to the same ZODB.&lt;/li&gt;
&lt;li&gt;You can think of the OID as (part of) the "address" of a particular
persistent object in the same way that &lt;tt class="docutils literal"&gt;id(obj)&lt;/tt&gt; returns the
memory address of an object in CPython. The OID is used to find or
store objects in the cache, and also to load the stored data for the
object.&lt;/li&gt;
&lt;li&gt;The root object has a special, pre-defined OID known as "z64".
That's our starting point for traversing the object graph.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can't see this just from the code, but ZODB defines OIDs to be 8
byte, or 64-bit, quantities. The special value z64 is just 8 zero
bytes in a row. ZODB has some utilities to convert between the 8 byte
form or a 64-bit number.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_026225205a25459d814131688217a98f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ZODB.Connection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;z64&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x00\x00\x00\x00\x00\x00\x00\x00'&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_oid&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x00\x00\x00\x00\x00\x00\x00\x00'&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ZODB.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;p64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u64&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;0&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;p64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_026225205a25459d814131688217a98f-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x00\x00\x00\x00\x00\x00\x00\x00'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Each persistent object knows its own OID and keeps track of it in a
&lt;a class="reference external" href="https://persistent.readthedocs.io/en/latest/api/interfaces.html#persistent.interfaces.IPersistent._p_oid"&gt;special attribute known as _p_oid&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_oid&lt;/span&gt;
&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x00\x00\x00\x00\x00\x00\x00\x00'&lt;/span&gt;
&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_oid&lt;/span&gt;
&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'&amp;gt;.\xe6\x8c\x1a\xb9\xf8\xd3'&lt;/span&gt;
&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_0c75e3fa02e8416b8b8ea80bcae995f4-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;4480772168698427603&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="storage-details-and-referencing-objects"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id3"&gt;Storage Details and Referencing Objects&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;get()&lt;/tt&gt; method of a ZODB Connection returns persistent objects,
but it uses the lower level &lt;a class="reference external" href="https://zodb.org/en/latest/reference/storages.html#storage-interfaces"&gt;"storage" interface&lt;/a&gt;
to get the actual data stored for the object. Each ZODB Connection has
a storage object, and all the Connections of a particular ZODB DB have
storage objects that are reading and writing to the same shared data
(e.g., a single file for &lt;tt class="docutils literal"&gt;FileStorage&lt;/tt&gt;, or a single SQL database for
&lt;tt class="docutils literal"&gt;RelStorage&lt;/tt&gt;.) Among other things, the storage is responsible for
holding the data stored for the persistent objects. This data, or
state, is kept as a byte-string produced by &lt;a class="reference external" href="https://docs.python.org/3/library/pickle.html"&gt;pickle&lt;/a&gt;. We can use
&lt;a class="reference external" href="https://docs.python.org/3/library/pickletools.html"&gt;pickletools&lt;/a&gt; to peek at this.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pickletools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;    0: \x80 PROTO      3&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;    2: c    GLOBAL     'persistent.mapping PersistentMapping'&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;   40: q    BINPUT     0&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;   42: .    STOP&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;highest protocol among opcodes = 2&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;None&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pickletools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;    0: \x80 PROTO      3&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;    2: }    EMPTY_DICT&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;    3: q    BINPUT     1&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;    5: X    BINUNICODE 'data'&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;   14: q    BINPUT     2&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;   16: }    EMPTY_DICT&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-15"&gt;&lt;/a&gt;&lt;span class="go"&gt;   17: q    BINPUT     3&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;   19: X    BINUNICODE 'obj'&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-17"&gt;&lt;/a&gt;&lt;span class="go"&gt;   27: q    BINPUT     4&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-18"&gt;&lt;/a&gt;&lt;span class="go"&gt;   29: C    SHORT_BINBYTES b'&amp;gt;.\xe6\x8c\x1a\xb9\xf8\xd3'&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-19"&gt;&lt;/a&gt;&lt;span class="go"&gt;   39: q    BINPUT     5&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-20"&gt;&lt;/a&gt;&lt;span class="go"&gt;   41: c    GLOBAL     'persistent Persistent'&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-21"&gt;&lt;/a&gt;&lt;span class="go"&gt;   64: q    BINPUT     6&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-22"&gt;&lt;/a&gt;&lt;span class="go"&gt;   66: \x86 TUPLE2&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-23"&gt;&lt;/a&gt;&lt;span class="go"&gt;   67: q    BINPUT     7&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-24"&gt;&lt;/a&gt;&lt;span class="go"&gt;   69: Q    BINPERSID&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-25"&gt;&lt;/a&gt;&lt;span class="go"&gt;   70: s    SETITEM&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-26"&gt;&lt;/a&gt;&lt;span class="go"&gt;   71: s    SETITEM&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-27"&gt;&lt;/a&gt;&lt;span class="go"&gt;   72: .    STOP&lt;/span&gt;
&lt;a name="rest_code_b52c6f75d6e0495d8a84a68b636190f9-28"&gt;&lt;/a&gt;&lt;span class="go"&gt;   highest protocol among opcodes = 3&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The important part here is the line labeled 29. Notice how that byte
string matches the OID of our second persistent object. We can see
that our root persistent object is referring to our other persistent
object via its OID. The opcode labeled 69, &lt;tt class="docutils literal"&gt;BINPERSID&lt;/tt&gt; is where ZODB
hooks in to the unpickling process. By setting an &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/_compat.py#L95"&gt;attribute on the
unpickler called "persistent_load"&lt;/a&gt;,
ZODB &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/serialize.py#L490"&gt;arranges to be called&lt;/a&gt;
any time one of these OIDs is located in a pickle. ZODB can then go
back to the cache or the storage to find the matching object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conflict-detection-through-serial-numbers"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id4"&gt;Conflict Detection Through Serial Numbers&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We've seen how ZODB can address objects, finding them and loading them
by OID, automatically re-creating in memory the object graph we built.
But that's not all ZODB does. ZODB provides the ability to modify and
save objects, and to detect when more than one Connection attempts to
do so at the same time, resulting in a conflict. How does this work?&lt;/p&gt;
&lt;p&gt;To explore this, lets have both connections make a change to &lt;em&gt;their&lt;/em&gt;
version of the root object. We'll be using separate transaction
managers, one for each connection, in order to do this.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;transaction&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TransactionManager&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn1_txm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TransactionManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2_txm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TransactionManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;None&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;None&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn1_txm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn2_txm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj2'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_2700bff5685943638e07ba3c80658e88-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s1"&gt;'obj3'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Persistent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If we commit the changes from one transaction, everything goes as
planned:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_52448c7218964c6bbffa88e912428a5d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn1_txm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_52448c7218964c6bbffa88e912428a5d-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;But trying to commit the other transaction fails because the object
has already been modified in an incompatible way:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2_txm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-2"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-3"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-4"&gt;&lt;/a&gt;&lt;span class="gr"&gt;   raise ConflictError(oid=oid, serials=(committedSerial, oldSerial),&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-5"&gt;&lt;/a&gt;&lt;span class="gr"&gt;ZODB.POSException.ConflictError&lt;/span&gt;: &lt;span class="n"&gt;database conflict error&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;    (oid 0x00,&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;    class persistent.mapping.PersistentMapping,&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;    serial this txn started with 0x03dfd117d4dbf099 2021-05-03 16:23:49.888861,&lt;/span&gt;
&lt;a name="rest_code_e11e68c93fdb412aa2788a9099c5912e-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;    serial currently committed 0x03dfd123995e91dd 2021-05-03 16:35:35.945956)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;How did ZODB know the object had changed? In addition to keeping track
of its OID, each persistent object also &lt;a class="reference external" href="https://persistent.readthedocs.io/en/latest/api/interfaces.html#persistent.interfaces.IPersistent._p_serial"&gt;keeps track of its serial
number in the _p_serial special attribute&lt;/a&gt;.
When one connection tries to make a change to an object, the serial
number of the object being changed is compared with the serial number
ZODB has stored for the object, and if they're not equal, there's a
conflict. (The &lt;tt class="docutils literal"&gt;Connection.readCurrent()&lt;/tt&gt; API does the same thing for
objects that haven't been modified.)&lt;/p&gt;
&lt;p&gt;We can confirm this by matching the &lt;tt class="docutils literal"&gt;_p_serial&lt;/tt&gt; of each connection's
object with the error message reported above.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x03\xdf\xd1\x17\xd4\xdb\xf0\x99'&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;'0x3dfd117d4dbf099'&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x03\xdf\xd1#\x99^\x91\xdd'&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d902e1815b4448b18aaa3da63edee296-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;'0x3dfd123995e91dd'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;That serial number is the transaction ID.&lt;/strong&gt; This is sometimes
abbreviated to TID.&lt;/p&gt;
&lt;p&gt;Thus, to uniquely identify a particular revision of a particular
object, we need both its OID and TID. (Together, they form the full
"address" of the object.) The storage object has methods
to load object states as they existed at particular serial numbers:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadSerial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;(b'\x80\x03cpersistent.mapping\nPersistentMapping\nq\x00.\x80\x03}q\x01'&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-3"&gt;&lt;/a&gt;&lt;span class="go"&gt; b'X\x04\x00\x00\x00dataq\x02}q\x03X\x03\x00\x00\x00objq\x04C\x08&amp;gt;.'&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-4"&gt;&lt;/a&gt;&lt;span class="go"&gt; b'\xe6\x8c\x1a\xb9\xf8\xd3q\x05cpersistent\nPersistent\nq\x06\x86q\x07Qss.')&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loadSerial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;z64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;(b'\x80\x03cpersistent.mapping\nPersistentMapping\nq\x00.\x80\x03}q\x01'&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-7"&gt;&lt;/a&gt;&lt;span class="go"&gt; b'X\x04\x00\x00\x00dataq\x02}q\x03(X\x03\x00\x00\x00objq\x04C\x08&amp;gt;'&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-8"&gt;&lt;/a&gt;&lt;span class="go"&gt; b'.\xe6\x8c\x1a\xb9\xf8\xd3q\x05cpersistent\nPersistent\nq\x06\x86q\x07QX\x04'&lt;/span&gt;
&lt;a name="rest_code_52b0e8ad116c401d8a7ae09aeafd6316-9"&gt;&lt;/a&gt;&lt;span class="go"&gt; b'\x00\x00\x00obj2q\x08C\x08&amp;gt;.\xe6\x8c\x1a\xb9\xf8\xd4q\th\x06\x86q\nQus.')&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="setting-the-serial-number"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id5"&gt;Setting the Serial Number&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You might be wondering when the &lt;tt class="docutils literal"&gt;_p_serial&lt;/tt&gt; of a persistent object
is set. There are two times. First, when an object has been added or
modified during a transaction, one of the last things ZODB does is
update the &lt;tt class="docutils literal"&gt;_p_serial&lt;/tt&gt;. &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/Connection.py#L690"&gt;Here's part of&lt;/a&gt;
&lt;tt class="docutils literal"&gt;tpc_finish&lt;/tt&gt; (the &lt;em&gt;last&lt;/em&gt; part of committing a transaction) from
&lt;tt class="docutils literal"&gt;Connection&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tpc_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-2"&gt;&lt;/a&gt;    &lt;span class="sd"&gt;"""Indicate confirmation that the transaction is done.&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;serial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tpc_finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-6"&gt;&lt;/a&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;oid_iterator&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_modified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_creating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-7"&gt;&lt;/a&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;oid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oid_iterator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-8"&gt;&lt;/a&gt;            &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-9"&gt;&lt;/a&gt;            &lt;span class="c1"&gt;# Ignore missing objects and don't update ghosts.&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-10"&gt;&lt;/a&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_changed&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-11"&gt;&lt;/a&gt;                &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a name="rest_code_1c6160c83e984d89b5e4752229061f5e-12"&gt;&lt;/a&gt;                &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_p_serial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serial&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Notice that it's the underlying storage that's responsible for
allocating the transaction ID. Likewise, the underlying storage is
responsible for allocating new OIDs when objects are first stored.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="properties-of-transaction-ids"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id6"&gt;Properties of Transaction IDs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From the examples above, we've learned a few things about transaction
IDs.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Like OIDs, TIDs are also 8-byte, or 64-bit, numbers (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;b'\x03\xdf\xd1\x17\xd4\xdb\xf0\x99'&lt;/span&gt;&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;They can be printed as those 8 bytes, but more often they are
printed as a number (&lt;tt class="docutils literal"&gt;279171602205896857&lt;/tt&gt;), usually in hexadecimal
(&lt;tt class="docutils literal"&gt;0x3dfd117d4dbf099&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;Most interestingly, transaction IDs can also be interpreted as
points in UTC time (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;2021-05-03&lt;/span&gt; 16:23:49.888861&lt;/tt&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://github.com/zopefoundation/persistent/blob/7608751e596978bb25d6d8a072ecdfaf52d0f52f/persistent/timestamp.py#L103"&gt;persistent.timestamp.TimeStamp&lt;/a&gt;
class assists with parsing and formatting TIDs.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;persistent.timestamp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TimeStamp&lt;/span&gt;
&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeStamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\x03\xdf\xd1\x17\xd4\xdb\xf0\x99&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;2021-05-03 16:23:49.888861&lt;/span&gt;
&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_4b142e03d3a644f68849638c58782ac5-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;1620059029.888861&lt;/span&gt;
&lt;/pre&gt;&lt;div class="section" id="transaction-ids-are-based-on-the-current-time"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id7"&gt;Transaction IDs Are Based On the Current Time&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a transaction commits, ZODB assigns it a transaction ID based on
the current time. &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/1fb097b41cae4ca863c8a3664414c9ec0e204393/src/ZODB/BaseStorage.py#L199"&gt;This is part of&lt;/a&gt;
&lt;tt class="docutils literal"&gt;tpc_begin&lt;/tt&gt; (the &lt;em&gt;first&lt;/em&gt; part of committing a transaction) from the
&lt;tt class="docutils literal"&gt;BaseStorage&lt;/tt&gt; class, a common base class for many storage implementations:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tpc_begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-2"&gt;&lt;/a&gt;    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-4"&gt;&lt;/a&gt;        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-5"&gt;&lt;/a&gt;        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeStamp&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;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&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;now&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,)))&lt;/span&gt;
&lt;a name="rest_code_0967b7d157a54453af4f5ede4605166c-6"&gt;&lt;/a&gt;        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="transaction-ids-must-increase"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id8"&gt;Transaction IDs Must Increase&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each transaction that commits gets a new transaction ID, based on the
current time. Since time moves forward (later is always later than
now), that suggests that subsequent transaction IDs will just get
bigger and bigger. And normally that would be true. But suppose the
computer's clock gets set back, or suppose the database file is moved
from one machine whose clock was running way ahead to another machine
whose clock is set to the right time. How do we guarantee that a value
derived from &lt;tt class="docutils literal"&gt;time.time()&lt;/tt&gt; is actually going to be larger than the
last value?&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;BaseStorage&lt;/tt&gt; and the &lt;tt class="docutils literal"&gt;TimeStamp&lt;/tt&gt; class have us covered.
&lt;tt class="docutils literal"&gt;BaseStorage&lt;/tt&gt; keeps track of the last TID that's been committed, and
&lt;tt class="docutils literal"&gt;TimeStamp&lt;/tt&gt; has a &lt;tt class="docutils literal"&gt;laterThan&lt;/tt&gt; method that guarantees that the
returned value is larger than the previous value:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tpc_begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-2"&gt;&lt;/a&gt;    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-4"&gt;&lt;/a&gt;        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-5"&gt;&lt;/a&gt;        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeStamp&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;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gmtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&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;now&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,)))&lt;/span&gt;
&lt;a name="rest_code_f0907b55d8bf4a55a2faf90cce45c7d9-6"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;div class="section" id="different-transaction-ids-can-have-the-same-timestamp"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id9"&gt;Different Transaction IDs Can Have the Same Timestamp&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;But there's a gotcha here. Just because the value is larger doesn't
mean that it will actually print as a different time. The conversion
from 64-bit number to floating point number of seconds since the epoch
to human-readable string is &lt;em&gt;lossy&lt;/em&gt;. We can see this if we continue
the example we started above.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;laterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x03\xdf\xd1\x17\xd4\xdb\xf0\x99'&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;b'\x03\xdf\xd1\x17\xd4\xdb\xf0\x9a'&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;2021-05-03 16:23:49.888861&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;2021-05-03 16:23:49.888861&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;later&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;1620059029.888861&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-14"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_4b87a5ebe09e493098be7abc179dc1d0-15"&gt;&lt;/a&gt;&lt;span class="go"&gt;1620059029.888861&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Even though the numeric value of &lt;tt class="docutils literal"&gt;later&lt;/tt&gt; is exactly one bigger than
that of &lt;tt class="docutils literal"&gt;ts&lt;/tt&gt;, they still print the same and have the same
&lt;tt class="docutils literal"&gt;timeTime()&lt;/tt&gt; value. Empirically, up to 16 sequential TID values can
have the same &lt;tt class="docutils literal"&gt;timeTime()&lt;/tt&gt; when using the C implementation of
&lt;tt class="docutils literal"&gt;TimeStamp&lt;/tt&gt;; the Python implementation rounds slightly differently
and up to &lt;em&gt;70&lt;/em&gt; sequential TID values have printed the same
&lt;tt class="docutils literal"&gt;timeTime()&lt;/tt&gt; (both implementations print the date string the same
for 70 sequential TIDs). The takeaway? Don't rely too heavily on the
&lt;tt class="docutils literal"&gt;timeTime()&lt;/tt&gt; or printed date string to compare or store TIDs. Prefer
instead to use either the &lt;tt class="docutils literal"&gt;raw()&lt;/tt&gt; 8-byte value or its 64-bit integer
representation.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="uses-of-transaction-ids"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id10"&gt;Uses of Transaction IDs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In addition to detecting conflicts, or loading particular revisions of
objects, ZODB can make explicit use of TIDs in other ways. Usually,
this is through their form as a timestamp. For example, the &lt;tt class="docutils literal"&gt;pack&lt;/tt&gt;
&lt;a class="reference external" href="https://zodb.org/en/latest/reference/storages.html#ZODB.interfaces.IStorage.pack"&gt;method of IStorage&lt;/a&gt;,
used to perform garbage collection, takes a "pack time" as its first
argument. This number of seconds is converted into a TID through the
&lt;tt class="docutils literal"&gt;TimeStamp&lt;/tt&gt; APIs by most storages. (But, because of the gotcha
outlined above, this is slightly approximate or potentially ambiguous.)&lt;/p&gt;
&lt;p&gt;Similarly, the &lt;tt class="docutils literal"&gt;open&lt;/tt&gt; &lt;a class="reference external" href="https://zodb.org/en/latest/reference/zodb.html#ZODB.DB.open"&gt;method of ZODB.DB&lt;/a&gt; can
accept an optional &lt;tt class="docutils literal"&gt;before=&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;at=&lt;/tt&gt; parameter to request a
historical view of the database. These parameters can be a specific
&lt;tt class="docutils literal"&gt;datetime.datetime&lt;/tt&gt; object representing an approximate timestamp, or
they can be an exact 8-byte TID.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="transaction-ids-in-relstorage"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html#id11"&gt;Transaction IDs in RelStorage&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Earlier, I showed the code that &lt;tt class="docutils literal"&gt;BaseStorage&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;FileStorage&lt;/tt&gt;
use to create a new TID based on the current time. Since
&lt;tt class="docutils literal"&gt;FileStorage&lt;/tt&gt; can only be used by a single process on a single
computer at a time, it's easy to guarantee that the TID is always
increasing.&lt;/p&gt;
&lt;p&gt;RelStorage is a bit different. The underlying SQL database in
RelStorage can be used by many different processes on many different
computers at the same time. There's no guarantee that all these
computers will have their clocks set exactly the same, so trying to
use the local time from each of them to create timestamps can lead to
confusion (TIDs might seem to have large gaps or otherwise jump
around, or may not even be related to the actual commit time at all!).&lt;/p&gt;
&lt;p&gt;To combat this, RelStorage computes the next TID on the database
server itself (for supported databases, namely &lt;a class="reference external" href="https://github.com/zodb/relstorage/blob/master/src/relstorage/adapters/mysql/procs/make_tid_for_epoch.sql"&gt;MySQL&lt;/a&gt;
and &lt;a class="reference external" href="https://github.com/zodb/relstorage/blob/master/src/relstorage/adapters/postgresql/procs/make_tid_for_epoch.sql"&gt;PostgreSQL&lt;/a&gt;).
That way, there's only one clock involved.&lt;/p&gt;
&lt;p&gt;RelStorage's client-side pickle cache is highly integrated with TIDs,
but that's another post.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>python</category><category>relstorage</category><category>zodb</category><guid>https://seecoresoftware.com/blog/2021/05/transaction-ids-in-zodb-and-relstorage.html</guid><pubDate>Mon, 03 May 2021 11:31:09 GMT</pubDate></item><item><title>RelStorage 3.0</title><link>https://seecoresoftware.com/blog/2019/11/relstorage-30.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;figure&gt;&lt;img src="https://seecoresoftware.com/blog/2019/11/relstorage.jpg"&gt;&lt;/figure&gt; &lt;div&gt;&lt;p&gt;We're happy to announce the release of RelStorage 3.0, the relational
storage engine for ZODB. Compared to RelStorage 2, highlights include
a 30% reduction in memory usage, and up to 98% faster performance!
(Ok, yes, that's from one specific benchmark and not everything is 98%
faster, but improved performance was a major goal.)&lt;/p&gt;
&lt;p&gt;RelStorage 3.0 is a major release of RelStorage with a focus on
performance and scalability. It's the result of a concentrated development
effort spanning six months, with each pre-release being in production
usage with large databases.&lt;/p&gt;
&lt;p&gt;Read on to find out what's new.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#backwards-incompatible-changes" id="id7"&gt;Backwards Incompatible Changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#major-changes" id="id8"&gt;Major Changes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#pickle-cache" id="id9"&gt;Pickle Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#persistent-cache" id="id10"&gt;Persistent Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#zodb-5-parallel-commit" id="id11"&gt;ZODB 5 Parallel Commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#zodb-5-prefetch" id="id12"&gt;ZODB 5 Prefetch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#support-for-sqlite" id="id13"&gt;Support for SQLite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#performance-improvements" id="id14"&gt;Performance Improvements&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#writing" id="id15"&gt;Writing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#reading" id="id16"&gt;Reading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#gevent" id="id17"&gt;gevent&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#minor-changes" id="id18"&gt;Minor Changes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#supported-versions" id="id19"&gt;Supported Versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#the-blob-cache" id="id20"&gt;The Blob Cache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#packing-and-gc" id="id21"&gt;Packing and GC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#performance-grab-bag" id="id22"&gt;Performance Grab Bag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#conclusion" id="id23"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p class="rubric"&gt;Overview&lt;/p&gt;
&lt;p&gt;Please note that this document is only an overview. For details on the
extensive changes since the previous release, RelStorage 2.1, and this
one, please view the detailed &lt;a class="reference external" href="https://relstorage.readthedocs.io/en/latest/changelog.html#a1-2019-06-12"&gt;changelog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you're not familiar with ZODB (the native Python object database)
and how it uses pluggable storage engines like RelStorage, please take
a moment to review &lt;a class="reference external" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html"&gt;this introduction&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This document will cover a few important things to know about
RelStorage 3.0, and then go over some of the most important changes in
it. Next, we'll show how those changes affect performance. Finally,
we'll wrap it up with a whirlwind tour of some of the minor changes.&lt;/p&gt;
&lt;div class="section" id="backwards-incompatible-changes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id7"&gt;Backwards Incompatible Changes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we get to &lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#the-good-stuff"&gt;the good stuff&lt;/a&gt;, it's important to highlight the
small number of backwards incompatible changes and other things to be
aware of when migrating from RelStorage 2 to RelStorage 3.&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Schema Changes&lt;/p&gt;
&lt;p&gt;In history preserving schemas, the &lt;tt class="docutils literal"&gt;empty&lt;/tt&gt; column of the
&lt;tt class="docutils literal"&gt;transaction&lt;/tt&gt; table has been renamed to &lt;tt class="docutils literal"&gt;is_empty&lt;/tt&gt;. In MySQL
8.0.4, &lt;tt class="docutils literal"&gt;empty&lt;/tt&gt; became a reserved word. The table is altered
automatically when first opened with RelStorage 3.0. This makes the
schema incompatible with opening under RelStorage 2. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f1" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A new table, used during the commit process, is also automatically
added when first opened with RelStorage 3.&lt;/p&gt;
&lt;p&gt;Under MySQL, any remaining tables that were using the MyISAM engine
are converted to InnoDB when the schema is first opened. The only
tables remaining that were MyISAM were the pack tables and the
&lt;tt class="docutils literal"&gt;new_oid&lt;/tt&gt; table, all of which should ordinarily be empty, so this
conversion shouldn't take long.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Option Changes&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;shared-blob-dir&lt;/span&gt;&lt;/tt&gt; default has changed from true to false. If
you were using a non-shared blob-dir, meaning that blobs were only
stored on the filesystem, you'll need to explicitly set this to
true. The previous default could easily lead to accidental data
loss, and there is now a performance penalty for a true value. See
&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#blob-cache"&gt;blob_cache&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;The previously deprecated option &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;poll-interval&lt;/span&gt;&lt;/tt&gt; has been removed.&lt;/p&gt;
&lt;p&gt;Several of the cache persistence options are now deprecated and
ignored. They'll generate a warning on startup if found in the
configuration.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Concurrent Deployment With RelStorage 2 Is Not Possible&lt;/p&gt;
&lt;div class="admonition caution"&gt;
&lt;p class="first admonition-title"&gt;Caution!&lt;/p&gt;
&lt;p class="last"&gt;It is not possible for RelStorage 3 to write to a
database at the same time that RelStorage 2 is writing
to it.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The specifics around locking have changed entirely, and are not
compatible between the two versions. If RelStorage 3 and RelStorage 2
are both writing to a database, corruption is the very likely result.
For this reason, shutting down all RelStorage 2 instances, or at least
placing them into read-only mode, is required.&lt;/p&gt;
&lt;p&gt;RelStorage does not take specific steps to prevent this. It is up to
you to ensure any RelStorage 2 instances are shutdown or at least
read-only before deploying RelStorage 3.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="major-changes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id8"&gt;Major Changes&lt;/a&gt;&lt;/h2&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Benchmarking Notes&lt;/p&gt;
&lt;p&gt;The benchmark data was collected with &lt;a class="reference external" href="https://zodbshootout.readthedocs.io/en/latest/"&gt;zodbshootout&lt;/a&gt; 0.8. Recent
revisions of zodbshootout have adopted &lt;a class="reference external" href="https://pyperf.readthedocs.io/en/latest/"&gt;pyperf&lt;/a&gt; as the underlying
benchamrk engine. This helps ensure much more stable, consistent
results. It also allows collecting a richer set of data for later
analysis. The data was passed through the &lt;a class="reference external" href="https://seaborn.pydata.org/index.html"&gt;seaborn&lt;/a&gt; statistical
visualization library, which is built using &lt;a class="reference external" href="https://pandas.pydata.org"&gt;pandas&lt;/a&gt;, &lt;a class="reference external" href="https://numpy.org"&gt;numpy&lt;/a&gt; and
&lt;a class="reference external" href="https://matplotlib.org"&gt;matplotlib&lt;/a&gt;, to produce the plots shown here.&lt;/p&gt;
&lt;p&gt;For the comparisons between RelStorage 3 and RelStorage 2, the
RDBMS servers (MySQL 8 and PostgreSQL 11) were run on one computer
running Gentoo Linux using SSD storage. The RelStorage client was
run on a different computer, and the two were connected with
gigabit ethernet. The machines were otherwise idle.&lt;/p&gt;
&lt;p&gt;The comparisons used Python 2.7.16 (because 2.7 was the only
version of Python that had a native code gevent driver compatible
with RelStorage 2). The database drivers were mysqlclient 1.4.4 and
psycopg2 2.8.4. The gevent driver used &lt;a class="reference external" href="https://github.com/NextThought/ultramysql/@6e4bf3af30493a3543e45b691db6132675421306"&gt;this version of ultramysql&lt;/a&gt;,
umysqldb 1.0.4.dev2, PyMySQL 0.9.3 and gevent 1.5a2.&lt;/p&gt;
&lt;p&gt;Why transaction of size 1, 5 and 20 objects? Review of a database
containing 60 million objects showed the average transaction
involved 2.6 objects with a standard deviation of 7.4. A database
with 30 million objects had an average transaction of 6.1 objects
and a standard deviation of 13.&lt;/p&gt;
&lt;p&gt;In all the examples that follow, results for PostgreSQL are green
while those for MySQL are in blue. The darker shades are RelStorage
3, while the lighter shades are RelStorage 2. The y axis is time,
and shorter bars are better (no units or tickmarks are shown
because the scale differs between graphs and we're generally
focused on deltas not absolute values); the black line that appears
in the middle of some bars is the confidence interval. Click for a
larger version.&lt;/p&gt;
&lt;p&gt;Most examples show a cross section of RDBMS server by concurrency
kind (threads or process) by concurrency level (1, 5, or 20
concurrent threads or processes) by object count (1, 5 or 20
objects).&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils"&gt;
&lt;p&gt;To accomplish its goals of improving performance (especially under
high loads distributed across many processes on many machines),
reducing memory usage, and being usable in small environments without
access to a full RDBMS server (such as containers or test
environments), RelStorage features several major internal changes.&lt;/p&gt;
&lt;div class="section" id="pickle-cache"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id9"&gt;Pickle Cache&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The shared in-memory pickle cache has been redesigned to be precise
and MVCC based; it no longer uses the old checkpoint system. This
means that old revisions of objects can proactively be removed from
the cache when they are no longer needed. Together, this means that
connections within a process are able to share polling information,
with the upshot being that there are no longer large, stop-the-world
poll queries in order to rebuild checkpoints. Individual poll queries
are usually smaller too.&lt;/p&gt;
&lt;p&gt;It has also been changed to have substantially less overhead for each
cached object value. Previously, it would take almost 400 bytes of
overhead to store one cache entry. In examining a database of 60
million objects, it turned out that the average object size was only a
little over 200 bytes. Using 400 bytes to store 200 bytes was
embarrassing, and because the cache limit computations didn't take the
overhead into account it meant that if you configured an in-memory
cache size of 200MB, the cache could actually occupy up to 600MB.&lt;/p&gt;
&lt;p&gt;Now, storing a cached value needs only a little more than 100 bytes,
and the exact amount of overhead is included when enforcing the cache
limit (so a 200MB limit means 200MB of memory usage). The Python
cache implementation using a CFFI-based Segmented LRU and Python
lookup dictionary were replaced with a minimal Cython extension using
a C++ &lt;a class="reference external" href="https://www.boost.org/doc/libs/1_71_0/doc/html/intrusive/set_multiset.html"&gt;boost.intrusive list and map&lt;/a&gt;.
This also essentially eliminates the cache's impact on the Python
garbage collector, which should improve garbage collection times &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f6" id="id2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;!-- I don't have complete memory information for RelStorage 2, but it
should be similar to what I do have, which is RelStorage 3.0a11
(CFFI) and 3.0b3 (C++).

The memory usage comes from two machines with a master gunicorn
and 6 workers each (7 datapoints), as reported by __poll_old_oids_and_remove.
This is the last step of loading the persistent cache (around
800,000 objects) and is the total memory used by the process at
that point. This was after a code deployment, but opening the
database is the first step in booting a worker so this doesn't
include any application data sizes. Total memory with RelStorage
3.0a11 from all data points was 18,867MB. Total memory with
RelStorage 3.0b3 was 11,998MB, or about 64% - - - a reduction of
36%.

The more direct measurement of 'read_from_sqlite' gives data
points of 7902MB before and 7244MB after, or about 10%. (It's
faster too, from 3.3s to 0.9s for the '_bulk_update' and from 12s
to 6s for the enclosing 'read_from_sqlite'.) What's the difference
in memory usage between read_from_sqlite and poll_old_oids, which
is called essentially immediately after? I'm not entirely sure.
There's the 'minimum_oid' dict but that's about 55MB

Looking at prometheus stats
``sum(node_memory_Active_bytes{instance=~"ds.*"})`` shows a
similar reduction across all environments (restarts don't exactly
line up but a 20% or more change is clearly visible.) --&gt;
&lt;p&gt;In concrete terms, one set of production processes that required
almost 19GB of memory now requires only about 12GB: a 36% reduction.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="persistent-cache"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id10"&gt;Persistent Cache&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Along with rearchitecting the in-memory cache, the on-disk persistent
cache has been rebuilt on top of SQLite. Its hit rate is much
improved and can easily reach 100% if nothing in the database changed.
If you haven't deployed the persistent cache before, now would be a
great time to give it a try.&lt;/p&gt;
&lt;p&gt;If you had used the persistent cache in the past, the new cache should
just work. Old cache files will be ignored and you might want to
manually remove any that exist to reclaim disk space.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="zodb-5-parallel-commit"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id11"&gt;ZODB 5 Parallel Commit&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RelStorage 3 now requires ZODB 5, and implements ZODB 5's parallel
commit feature. During most of the ZODB commit process, including
conflict resolution, only objects being modified are exclusively
locked. Objects that were provided to &lt;tt class="docutils literal"&gt;Connection.readCurrent()&lt;/tt&gt; are
locked only in share mode so they may be locked that way by several
transactions at the same time. (This fact is particularly important
because &lt;a class="reference external" href="https://github.com/zopefoundation/BTrees/blob/master/BTrees/_base.py#L932"&gt;BTrees call&lt;/a&gt; &lt;tt class="docutils literal"&gt;readCurrent()&lt;/tt&gt; for every node traversed while
searching for the correct leaf node to add/remove/update, meaning
there can be a surprising amount of contention.)&lt;/p&gt;
&lt;p&gt;Only at the very end of the ZODB commit process when it is time to
commit to the database server is a database-wide lock taken while the
transaction ID is allocated. This should be a very brief time, so
transactions that are operating on distinct sets of objects can
continue concurrently for much longer (especially if conflicts occur
that must be resolved; one thread resolving conflicts no longer
prevents other threads from resolving non-overlapping conflicts).&lt;/p&gt;
&lt;p&gt;This works on most databases, but it works best on a database that
supports &lt;tt class="docutils literal"&gt;NOWAIT&lt;/tt&gt; share locks, like MySQL 8 or PostgreSQL. SQLite
doesn't support object-level locking or parallel commit. Oracle
doesn't support shared object-level locks.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="zodb-5-prefetch"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id12"&gt;ZODB 5 Prefetch&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RelStorage 3 implements efficient object prefetching through
&lt;tt class="docutils literal"&gt;Connection.prefetch()&lt;/tt&gt;. This is up to 74% faster than reading
objects individually on demand.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_cold_prefetch.png"&gt;&lt;img alt="Prefetching is much faster than reading serially." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_cold_prefetch.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;RelStorage 2 did not implement prefetch so this benchmark falls
back to reading objects individually. RelStorage 3 is able to query
the database in a single bulk operation.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="support-for-sqlite"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id13"&gt;Support for SQLite&lt;/a&gt;&lt;/h3&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The SQLite support is relatively new and hasn't received much
production-level testing.&lt;/p&gt;
&lt;p class="last"&gt;On some systems, the underlying sqlite3 module may experience
crashes when lots of threads are used (even though a ZODB
connection and its RelStorage instance and sqlite connection are
not thread safe and must only ever be used from a single thread at
a time, the sequential use from multiple threads can still cause
issues).&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;RelStorage 3 can use a local SQLite3 database file. I'll quote the
&lt;a class="reference external" href="https://relstorage.readthedocs.io/en/latest/faq.html#sqlite"&gt;RelStorage FAQ&lt;/a&gt; to explain why:&lt;/p&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Why does RelStorage support a SQLite backend? Doesn't that defeat the point?&lt;/p&gt;
&lt;p&gt;The SQLite backend fills a gap between FileStorage and an
external RDBMS server.&lt;/p&gt;
&lt;p&gt;FileStorage is fast, requires few resources, and has no external
dependencies. This makes it well suited to small applications,
embedded applications, or applications where resources are
constrained or ease of deployment is important (for example, in
containers).&lt;/p&gt;
&lt;p&gt;However, a FileStorage can only be opened by one process at a time.
Within that process, as soon as a thread begins committing, other
threads are locked out of committing.&lt;/p&gt;
&lt;p&gt;An external RDBMS server (e.g., PostgreSQL) is fast, flexible and
provides lots of options for managing backups and replications and
performance. It can be used concurrently by many clients on many
machines, any number of which can be committing in parallel. But
that flexibility comes with a cost: it must be setup and managed.
Sometimes running additional processes complicates deployment
scenarios or is undesirable (for example, in containers).&lt;/p&gt;
&lt;p&gt;A SQLite database combines the low resource usage and deployment
simplicity of FileStorage with the ability for many processes to
read from and write to the database concurrently. Plus, it's
typically faster than ZEO. The tradeoff: all processes using the
database must be on a single machine on order to share memory.&lt;/p&gt;
&lt;/div&gt;
&lt;hr class="docutils"&gt;
&lt;p&gt;I'll leave the rest to the FAQ, with the exception of these two
performance graphs showing SQLite slot in comfortably next to
FileStorage and ZEO.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_sqlite_add_objects.png"&gt;&lt;img alt="Adding objects in file-based storages." src="https://seecoresoftware.com/images/blog/2019/11/perf_sqlite_add_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;For small to medium sized write transactions, SQLite can actually
outperform FileStorage and ZEO when threads are in use. When
separate processes are in use, SQLite always beats ZEO.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_sqlite_read_cold.png"&gt;&lt;img alt="Reading objects in file-based storages." src="https://seecoresoftware.com/images/blog/2019/11/perf_sqlite_read_cold.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;When reading objects, SQLite is always faster than ZEO, but slower
than FileStorage.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="performance-improvements"&gt;
&lt;span id="the-good-stuff"&gt;&lt;/span&gt;&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id14"&gt;Performance Improvements&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Much effort was spent on improving RelStorage's performance, in terms
of overall speed and memory usage as well as concurrency and
scalability. Here, let's compare the performance of RelStorage 2.1.1 with
RelStorage 3.0 graphically from a speed perspective.&lt;/p&gt;
&lt;div class="section" id="writing"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id15"&gt;Writing&lt;/a&gt;&lt;/h3&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;RelStorage 2 failed to complete a number of the write
benchmarks that used 20 objects and 20 concurrent processes
or threads: some tasks would fail to obtain the commit lock
using the default 10 second timeout. Those tasks were
excluded from the results.&lt;/p&gt;
&lt;p class="last"&gt;RelStorage 3 did not have this problem.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;We'll start with writing to the database:&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_add_objects.png"&gt;&lt;img alt="Adding objects got much faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_add_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p&gt;When simply adding new objects to the database, PostgreSQL is between
29% and 72% faster. MySQL is essentially within the margin of error
for the non-concurrent cases, and up to 79% faster for larger, more
concurrent tests.&lt;/p&gt;
&lt;p&gt;Updating existing objects:&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_update_objects.png"&gt;&lt;img alt="Updating existing objects got much faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_update_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p&gt;When updating objects that already existed in the database, The
difference for MySQL ranges between statistically insignificant up to
to 79% faster.. PostgreSQL likewise ranges from statistical
insignificance and up to 80% faster.&lt;/p&gt;
&lt;p&gt;Handling conflicts received extra attention.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_update_conflicting_objects.png"&gt;&lt;img alt="Updating existing object that had conflicts got much faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_update_conflicting_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Updating objects that have to resolve conflicts.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Once again, some cases were statistically insignificant for both
databases. The cases that were statistically significant show a
15% to 84% improvement for PostgreSQL, with the range being from 11%
to 89% for MySQL. Not only is it faster, going by the much tighter
confidence intervals, it's also less volatile and more consistent.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reading"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id16"&gt;Reading&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RelStorage, like ZEO, includes a secondary pickle cache that's shared
amongst all the connections in a process. Here's what it looks like to
read data out of that cache (not hitting the database at all).&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_read_hot.png"&gt;&lt;img alt="Reading objects from RelStorage's cache got much faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_read_hot.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Reading from the pickle cache, by concurrency kind and object
count.&lt;/p&gt;
&lt;div class="legend"&gt;
Ideally the bars for both databases of a particular RelStorage
release would be equal heights in any given test case because this
test is database independent. That's not quite the case. When they're
not, they're within each other's confidence intervals.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Because the bottom row of graphs is separate processes, they don't
benefit much from the actual &lt;em&gt;sharing&lt;/em&gt; of the cache. The improvements
there, 30 – 40%, show the effect of the cache changes in isolation.
The top row of graphs shows the improvement in the intended use case,
when the cache is shared by multiple threads in a process. In that
case, the difference can be up to 90%. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f4" id="id3"&gt;[5]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Reading directly from the database, on the other hand, is harder to
qualify.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_comp_read_cold.png"&gt;&lt;img alt="Reading directly from the database is mixed." src="https://seecoresoftware.com/images/blog/2019/11/perf_comp_read_cold.thumbnail.png"&gt;&lt;/a&gt;
&lt;p&gt;Oh no! It looks like RelStorage 3 actually got &lt;em&gt;slower&lt;/em&gt; when reading
directly from the database. That's one of its core tasks. How could
that be allowed to happen? (Spoiler alert: it didn't.)&lt;/p&gt;
&lt;p&gt;Look closely at the pattern. In the top row, when we're testing with
threads, RelStorage 3 is always at least as good as RelStorage 2 and
frequently better &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f3" id="id4"&gt;[4]&lt;/a&gt;. It's only the bottom row, dealing with
processes, that RelStorage 3 looks bad. But as you move further to the
right, where more processes are making more queries to load more
objects, the gap begins to close. At 20 objects in 5 processes, the
gap is essentially gone. (And then we fall off a cliff at 20 processes
querying 20 objects. It's not entirely clear exactly what's going on,
but that's more CPUs/hardware threads than either the client machine
or server machine has so it's not surprising that efficiency begins to
fall.)&lt;/p&gt;
&lt;p&gt;It turns out the benchmark includes the cost of opening a ZODB
connection. For processes, that's a connection using a whole new ZODB
instance, so there will be no prior connections open. But for threads,
the ZODB instance is shared, so there will be other connections in the
pool.&lt;/p&gt;
&lt;p&gt;Working with brand new RelStorage connections got a bit slower in
RelStorage 3 compared to RelStorage 2. They use more &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Prepared_statement"&gt;prepared
statements&lt;/a&gt; (especially on PostgreSQL), and they use more database
session state (especially on MySQL). Performing the first poll of the
database state may also be a bit more expensive. So when the
connection doesn't get used to do much before being closed, these
costs outweigh the other speedups in RelStorage 3. But somewhere
between making 5 and 20 queries for objects, the upfront costs are
essentially amortized away. As always in ZODB, connection pooling with
appropriate pool settings is important.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="gevent"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id17"&gt;gevent&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In RelStorage 2, gevent was supported on Python 2 and Python 3 for
MySQL and PostgreSQL when using a pure-Python database driver
(typically PyMySQL for the former and pg8000 for the later). There was
special gevent support for MySQL using a custom database driver, but
only on Python 2. This driver took a hybrid approach, providing some C
acceleration of low-level operations, but delegating most operations to
PyMySQL.&lt;/p&gt;
&lt;p&gt;RelStorage 3 supports gevent-aware, fully native drivers, for both
PostgreSQL and MySQL on both Python 2 and Python 3. Moreover, the
MySQL driver has special support for RelStorage's two-phase commit
protocol, essentially boosting the priority of a greenlet that's
committing &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f5" id="id5"&gt;[6]&lt;/a&gt;. This avoid situations where a greenlet takes
database-wide locks and then yields control to a different greenlet
that starves the event loop, leaving the database locked for an
unacceptable amount of time and halting the forward progress of other
processes.&lt;/p&gt;
&lt;p&gt;Do these things make a difference? We can compare the performance of
gevent with MySQL to find out.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_add_objects.png"&gt;&lt;img alt="Adding objects is faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_add_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Adding objects, by concurrency type.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Adding objects improved across the board for all concurrency types.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_update_objects.png"&gt;&lt;img alt="Updating objects is faster." src="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_update_objects.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Updating objects, by concurrency type.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Updating objects improved across the board for all concurrency types.
Updating conflicting objects shows a similar gain.&lt;/p&gt;
&lt;div class="figure"&gt;
&lt;a class="reference external image-reference" href="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_read_cold.png"&gt;&lt;img alt="Reading from the database is largely unchanged." src="https://seecoresoftware.com/images/blog/2019/11/perf_gevent_read_cold.thumbnail.png"&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Reading objects, by concurrency type.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Reading individual objects, by contrast, shows no distinct trend. I
suspect that it's essentially unchanged (the transactional and polling
parts around it that were changed are not measured here), but we'd
need more samples to be able to properly show that.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="minor-changes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id18"&gt;Minor Changes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section documents some of the other changes in RelStorage 3.&lt;/p&gt;
&lt;div class="section" id="supported-versions"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id19"&gt;Supported Versions&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Support for PostgreSQL 12 and MySQL 8 were added, as well as support
for Python 3.8.&lt;/p&gt;
&lt;p&gt;Support for MySQL 5.6 and PostgreSQL 9.5 were removed, as was support
for old versions of ZODB. Also, RelStorage no longer depends on ZEO
(so it's theoretically possible that Python 2.7.8 and earlier could
run RelStorage, but this isn't tested or recommended).&lt;/p&gt;
&lt;p&gt;Most tested database drivers were updated to newer versions, and in
some cases the minimum supported versions were updated.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;mysqlclient must be 1.4, up from 1.3.7.&lt;/li&gt;
&lt;li&gt;psycopg2 must be 2.8, up from 1.6.1.&lt;/li&gt;
&lt;li&gt;psycopg2cffi must be 2.8.1, up from 2.7.4.&lt;/li&gt;
&lt;li&gt;cx_Oracle must be 6.0, up from 5.0.&lt;/li&gt;
&lt;li&gt;Support was removed for the Python 2-only driver &lt;tt class="docutils literal"&gt;umysqldb&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This table summarizes the support various databases have for
RelStorage 3 features.&lt;/p&gt;
&lt;style&gt;
table {border-color: transparent}
&lt;/style&gt;&lt;table border="1" class="table table-striped colwidths-auto docutils"&gt;
&lt;caption&gt;Supported Features&lt;/caption&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt; &lt;/th&gt;
&lt;th class="head"&gt;PostgreSQL&lt;/th&gt;
&lt;th class="head"&gt;MySQL&lt;/th&gt;
&lt;th class="head"&gt;Oracle&lt;/th&gt;
&lt;th class="head"&gt;SQLite&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;Parallel commit&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shared readCurrent locks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Non-blocking readCurrent locks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Native on MySQL 8,
emulated on MySQL
5.7&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;N/A (there is
no distinction in lock type)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Streaming blobs&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (emulated via
chunking)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (consider configuring a
shared-blob-dir)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Central transaction ID allocation&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (could probably be
implemented)&lt;/td&gt;
&lt;td&gt;N/A (but essentially yes
because it only involves one machine)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Atomic lock and commit without Python
involvement&lt;/td&gt;
&lt;td&gt;Yes (except
with
PG8000)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (could probably be
implemented)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="the-blob-cache"&gt;
&lt;span id="blob-cache"&gt;&lt;/span&gt;&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id20"&gt;The Blob Cache&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;shared-blob-dir&lt;/span&gt;&lt;/tt&gt; (where all blobs are &lt;em&gt;only&lt;/em&gt; stored on a
filesystem and never in the database) disables much of the parallel
commit features. This is because testing whether we can actually store
the blob successfully during the "vote" phase of ZODB's &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol"&gt;two-phase commit&lt;/a&gt;
requires knowing the transaction ID, and knowing the transaction ID
requires taking the database-wide commit lock. This is much sooner
than is otherwise required and the lock is held for much longer (e.g.,
during conflict resolution).&lt;/p&gt;
&lt;p&gt;Increasing popularity, and ever-growing databases, make the
implementation of the blob &lt;em&gt;cache&lt;/em&gt; all the more important. This
release focused on blob cache maintenance, specifically the process
whereby the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;blob-cache-size&lt;/span&gt;&lt;/tt&gt; (if any) is ensured.&lt;/p&gt;
&lt;p&gt;First, for history free databases, when a new revision of a blob is
uploaded to replace an older one, if RelStorage has the old revision
cached on disk and can determine that it's not in use, it will be
deleted as part of the commit process. This applies whether or not a
cache size limit is in place.&lt;/p&gt;
&lt;p&gt;If it becomes necessary to prune the blob cache, the process of doing
so has been streamlined. It spawns far fewer unnecessary threads than
it used to. If the process is using gevent, it uses an actual native
thread to do the disk scan and IO instead of a greenlet, which would
have blocked the event loop.&lt;/p&gt;
&lt;p&gt;Finally, if running the pruning process is still too expensive and the
thread interferes with the work of the process, there's a new option
that spawns a separate process to do the cleanup. This can also be
used manually to perform a cleanup without opening a storage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="packing-and-gc"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id21"&gt;Packing and GC&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;History-preserving databases now support &lt;a class="reference external" href="https://pypi.org/project/zc.zodbdgc/"&gt;zc.zodbdgc&lt;/a&gt; for
multi-database garbage collection.&lt;/p&gt;
&lt;p&gt;RelStorage's native packing is now safer for concurrent use in
history-free databases thanks to correcting several race conditions.&lt;/p&gt;
&lt;p&gt;For both types of databases, packing and pre-packing require
substantially less memory. Pre-packing a large database was measured to
use 9 times less memory on CPython 3 and 15 times less on CPython 2
(from 3GB to 200 MB).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="performance-grab-bag"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id22"&gt;Performance Grab Bag&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here's a miscellaneous selection of interesting changes, mostly performance
related.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Reduce the number of network communications with the database.&lt;/p&gt;
&lt;p&gt;RelStorage tries harder to avoid talking to the database more times
than necessary. Each round-trip introduces extra latency that was
measurable, even on fast connections. Also, native database drivers
usually release the GIL during a database operation, so there could
be extra overhead introduced acquiring it again. And under gevent,
making a query yields to the event loop, which is good, but it could
be an arbitrary amount of time before the greenlet regains control
to process the response. If locks are being held, too many queries
could spell disaster.&lt;/p&gt;
&lt;p&gt;This was accomplished in several ways. One way was to move larger
sequences of commands into stored procedures (MySQL and PostgreSQL
only). For example, previously to finish committing a transaction,
RelStorage required 7 database interactions in a history-preserving
database: 1 to acquire the lock, 1 to get the previous transaction
id, 1 to store transaction metadata, 1 to store objects, 1 to store
blobs, 1 to update the current object pointers, and finally one to
commit. Now, that's all handled by a single stored procedure using
one database operation. The Python process doesn't need to acquire
the GIL (or cycle through the event loop) to commit and release
locks, that happens immediately on the database server regardless of
how responsive the Python process is.&lt;/p&gt;
&lt;p&gt;More careful control of transactions eliminated several superflous
COMMIT or ROLLBACK queries in all databases. Similarly, more careful
tracking of allocated object identifiers (&lt;tt class="docutils literal"&gt;_p_oid&lt;/tt&gt;) eliminated
some unnecessary database updates.&lt;/p&gt;
&lt;p&gt;The use of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Merge_(SQL)"&gt;upserts&lt;/a&gt;, which eliminate at least one query, was
previously limited to a select few places for PostgreSQL and MySQL.
That has been extended to more places for all supported databases.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Allocate transaction IDs on the database server.&lt;/p&gt;
&lt;p&gt;This was primarily about reducing database communications. However,
because transaction IDs are based on the current time, it also has
the important side-effect of ensuring that they're more consistently
meaningful with only one clock to consider.&lt;/p&gt;
&lt;p&gt;Previously, all transaction IDs could be at least as inaccurate as
the least-accurate clock writing to the database (if that clock was
in the future).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;PostgreSQL uses the &lt;tt class="docutils literal"&gt;COPY&lt;/tt&gt; command to upload data.&lt;/p&gt;
&lt;p&gt;Specifically, it uses the &lt;a class="reference external" href="https://www.postgresql.org/docs/11/sql-copy.html#id-1.9.3.55.9.4"&gt;binary format&lt;/a&gt; of the bulk-loading
&lt;tt class="docutils literal"&gt;COPY&lt;/tt&gt; command to stream data to the server. This can improve
storage times by 20% or so.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id23"&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RelStorage 3 represents a substantial change from RelStorage 2. The
pickle cache—both in-memory and on-disk—has been
completely rewritten, the locking process has been re-imagined in
support of parallel commit, time-sensitive logic moved into stored
procedures, and more. Despite that, it should be a drop-in replacement
in most situations.&lt;/p&gt;
&lt;p&gt;Although RelStorage 3 has been in production usage under heavy load at
NextThought for its entire development cycle, and we haven't
encountered any problems with it that could lead to data loss, it's
still software, and all software has bugs. Please exercise appropriate
care when upgrading. &lt;a class="reference external" href="https://github.com/zodb/relstorage/issues"&gt;Bug reports&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/zodb/relstorage/pulls"&gt;pull requests&lt;/a&gt; are
encouraged and appreciated.&lt;/p&gt;
&lt;p&gt;We're very happy with the enhancements, especially around performance,
and hope those improvements are applicable to most users. We welcome
feedback on whether they are or are not, and also want to hear about
where else RelStorage could improve.&lt;/p&gt;
&lt;p&gt;Finally, I'd like to say thank you to everyone who has contributed to
the development of RelStorage 3, whether through testing pre-releases,
filing bug reports, or sharing enhancement ideas and use cases. It's
greatly appreciated.&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Why not simply &lt;a class="reference external" href="https://dev.mysql.com/doc/refman/8.0/en/identifiers.html"&gt;"quote" the reserved word?&lt;/a&gt;
Much of the SQL queries that RelStorage uses are shared among
all supported databases. By default, MySQL uses a different,
non-standard quoting syntax that wouldn't work with the other
databases. That can be changed by altering the &lt;a class="reference external" href="https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html"&gt;SQL Mode&lt;/a&gt;,
but I was trying to avoid having to do that. In the end it
turned out that another change &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#f2" id="id6"&gt;[3]&lt;/a&gt; forced the alteration of the
mode, so I should have just done that in the first place. But
since there are good reasons to prevent RelStorage 2 and 3
from ever trying to use the same database, the
incompatibility didn't seem like a big deal.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In CPython, the &lt;a class="reference external" href="https://rushter.com/blog/python-garbage-collector/"&gt;generational (cyclic) garbage collector&lt;/a&gt; uses
time proportional to the number of objects in a generation
(all objects in a generation are stored in a linked list).
The more objects that exist, the longer it takes to perform a
collection. In RelStorage 2, storing a value in the cache
required creating several objects, and the Python garbage
collector would have to examine these. The RelStorage 3 cache
does not create objects that the garbage collector needs to
traverse. Similar remarks hold for PyPy.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id6"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;That change was support for SQLite, which requires the entire
"transaction" table to be quoted. Amusingly, "transaction"
is a word reserved by the SQL standard, while "empty" is
not.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;With the exception of PostgreSQL using one thread for 20
objects. The large error bar indicates an outlier event.
We're working with relatively small sample sizes, so that
throws things off.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id3"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;RelStorage 2 implemented some of its cache functions using
native code called by &lt;a class="reference external" href="https://cffi.readthedocs.io"&gt;CFFI&lt;/a&gt;. When CFFI calls native
code, Python's GIL is dropped, allowing other threads to run
(as long as they weren't trying to use the cache, which used
Python locks for thread safety). In contrast, RelStorage 3
uses a thin layer of &lt;a class="reference external" href="https://cython.readthedocs.io"&gt;cython&lt;/a&gt; to call into C++ and does
&lt;em&gt;not&lt;/em&gt; drop the GIL—it depends on the GIL for thread
safety. The substantial speed improvement should outweigh the
loss of the tiny window where the GIL was dropped.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html#id5"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;We don't have that much granular control over the PostgreSQL
driver (psycopg2). With MySQL (mysqlclient), on a
connection-by-connection basis we can control if a particular
query is going to yield to gevent or block. But with
psycopg2, whether it yields or not is global to the entire
process.&lt;/p&gt;
&lt;p class="last"&gt;The reverse is that psycopg2 actually gives us exactly the
same control as a gevent socket (yield each time any
read/write would block), whereas in mysqlclient we can only
wait for the first packet to arrive/go, after that it blocks
for the duration (server-side cursors give us a bit more
control, allowing yields between fetching groups of rows).&lt;/p&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>mysql</category><category>oracle</category><category>postgresql</category><category>python</category><category>releases</category><category>relstorage</category><category>sql</category><category>zodb</category><guid>https://seecoresoftware.com/blog/2019/11/relstorage-30.html</guid><pubDate>Tue, 12 Nov 2019 11:18:52 GMT</pubDate></item><item><title>Introduction to ZODB Data Storage</title><link>https://seecoresoftware.com/blog/2019/10/intro-zodb.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;ZODB is a powerful native object database for Python, widely known for
its use in the &lt;a class="reference external" href="https://zope.readthedocs.io/en/latest/index.html"&gt;Zope&lt;/a&gt; web framework and
the &lt;a class="reference external" href="https://plone.org"&gt;Plone&lt;/a&gt; content management system. By
enabling transparent object graph persistence with no need to
predefine schemas, ZODB enables extremely flexible application
development. With pluggable storage engines such as &lt;a class="reference external" href="http://www.zodb.org/en/latest/reference/storages.html#filestorage"&gt;FileStorage&lt;/a&gt;,
&lt;a class="reference external" href="https://github.com/zopefoundation/ZEO"&gt;ZEO&lt;/a&gt;, and &lt;a class="reference external" href="https://relstorage.readthedocs.io"&gt;RelStorage&lt;/a&gt;, it also provides flexible ways to store data.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;p&gt;This post provides an introduction to ZODB, focusing on some of the
lower-level mechanics of storing data. This post doesn't discuss
&lt;a class="reference external" href="https://persistent.readthedocs.io"&gt;persistent objects&lt;/a&gt;.&lt;/p&gt;
&lt;div class="admonition admonition-disclaimer"&gt;
&lt;p class="first admonition-title"&gt;Disclaimer&lt;/p&gt;
&lt;p class="last"&gt;This was written in support of the &lt;a class="reference external" href="https://seecoresoftware.com/blog/2019/11/relstorage-30.html"&gt;RelStorage 3.0 release&lt;/a&gt; so it may be biased in that
direction. It is not an exhaustive list of all storage options. For
example, it doesn't discuss &lt;a class="reference external" href="https://neo.nexedi.com"&gt;NEO&lt;/a&gt;, a
distributed, redundant storage. Partial lists of included and
non-included storages may be found &lt;a class="reference external" href="http://www.zodb.org/en/latest/reference/storages.html"&gt;in the ZODB storage
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#what-is-zodb" id="id10"&gt;What is ZODB?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#what-is-a-zodb-storage" id="id11"&gt;What is a ZODB storage?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#filestorage" id="id12"&gt;FileStorage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#zeo" id="id13"&gt;ZEO&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#what-is-relstorage" id="id14"&gt;What is RelStorage?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#features" id="id15"&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#mvcc-and-history-free-storage" id="id16"&gt;MVCC and History Free Storage&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#q-a" id="id17"&gt;Q &amp;amp; A&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#conflict-resolution" id="id18"&gt;Conflict Resolution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#summary" id="id19"&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#updates" id="id20"&gt;Updates&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="what-is-zodb"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id10"&gt;What is ZODB?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.zodb.org/en/latest/"&gt;ZODB&lt;/a&gt; &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f1" id="id1"&gt;[1]&lt;/a&gt; is a native object database for Python, enabling
transparent object persistence. It provides the illusion of an
infinite memory space holding application-defined objects. That memory
space is shared between processes running at different times on the
same or different machine. Only those objects actually used are
brought into physical memory. Think of it something like operating
system &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Paging"&gt;paging&lt;/a&gt;, but for objects, and distributed across time and
space. (Apple's CoreData framework has a similar technique it calls "&lt;a class="reference external" href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html"&gt;faulting&lt;/a&gt;".)&lt;/p&gt;
&lt;p&gt;In addition, ZODB provides a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Database_transaction"&gt;transactional&lt;/a&gt; view of these objects with
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control"&gt;snapshot isolation&lt;/a&gt;. Any given connection to the database sees a
consistent view of &lt;em&gt;all&lt;/em&gt; the objects in the database (whether it reads
or writes to any particular object or not) as-of the moment it began.
When adding or updating objects, no changes are published and made
visible to other connections until the writing connection &lt;em&gt;commits&lt;/em&gt;
its transaction, at which point either all the changes are made
visible or none of them are. Existing connections that continue
reading (or even writing!) will still not see those changes; they're
"stuck" at the snapshot view of the objects they started with. (The
ability for readers to continue to be able to retrieve old data that's
been replaced in newer transactions is known as multi-version
concurrency control, or &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control"&gt;MVCC&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Many connections may be reading and writing to the database at once.
ZODB uses &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Optimistic_concurrency_control"&gt;optimistic concurrency control&lt;/a&gt;. Readers don't block other
readers or writers, and writers are allowed to proceed as if they were
the only one making changes right up until they commit. Writes are
defined to occur in a strict order. If a writer discovers that an
earlier transaction had modified objects that it too wants to modify,
a conflict occurs. Instead of just rolling back the writing
transaction and forcing it to start over, taking the modified object
into account, ZODB gives the application the chance to resolve the
conflict using a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Merge_(version_control)#Three-way_merge"&gt;three-way merge&lt;/a&gt; between the object as it existed
when the transaction began, the object that the connection wants to
commit, and the object that was committed by the other writer. Only if
it cannot do so is the transaction rolled back.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-is-a-zodb-storage"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id11"&gt;What is a ZODB storage?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZODB uses a pluggable storage architecture, allowing different ways to
store the objects it manages. Storage engines are responsible for
allocating persistent object identifiers (OIDs) for each object ZODB
manages, storing object state data &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f2" id="id2"&gt;[2]&lt;/a&gt; when an object is added or
changed &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f2a" id="id3"&gt;[3]&lt;/a&gt;, and later retrieving the data for that particular object
given its OID. The storage is also responsible for implementing
snapshot isolation, ordering (serializing) writes and assigning
incrementing transaction identifiers (TIDs), and detecting and
handling conflicting writes.&lt;/p&gt;
&lt;div class="section" id="filestorage"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id12"&gt;FileStorage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Out of the box, in addition to a few different transient (in-memory)
storage engines, ZODB comes with one persistent (on-disk) storage
engine. &lt;a class="reference external" href="http://www.zodb.org/en/latest/reference/storages.html#filestorage"&gt;FileStorage&lt;/a&gt; uses a single file to store an append-only
transaction log for all the objects in the database. An additional
in-memory and on-disk structure is used to record the relationship
between objects (OIDs) and the transactions (TIDs) they appear in.&lt;/p&gt;
&lt;p&gt;As an append-only file, writing to FileStorage can be quite fast. It
requires memory (and extra storage space) proportional to the size of
the database to record object positions for fast access. If that extra
index data isn't saved to disk, it requires time proportional to the
size of the database to scan the file on startup to re-create that
index.&lt;/p&gt;
&lt;p&gt;Because of its append-only nature, previous versions of objects are
still found in the file and can be accessed by providing a proper TID.
A FileStorage is thus said to be "history preserving." That's how
snapshot isolation is implemented: each connection is explicitly
associated with a TID and when it needs to read an object it asks the
FileStorage to provide the revision of the object most recently
written &lt;em&gt;before&lt;/em&gt; that TID &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f3" id="id4"&gt;[4]&lt;/a&gt;. This can also be used like a
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Version_control"&gt;version control system&lt;/a&gt; to view and even recover or undo changes to
objects. Periodically, a FileStorage must be "packed" to remove
obsolete historical data and prevent the file from growing forever.&lt;/p&gt;
&lt;p&gt;FileStorage is widely deployed and has a long history of stability. It
can only be used by a single process at a time, however. Within that
process, only a single thread can be in the process of committing a
transaction at a time (FileStorage uses a database-wide lock to
provide serialization).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="zeo"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id13"&gt;ZEO&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A common method to extend access to a FileStorage to more than one
process and/or to more than one machine is to deploy a &lt;a class="reference external" href="https://github.com/zopefoundation/ZEO"&gt;ZEO&lt;/a&gt; &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f4" id="id5"&gt;[5]&lt;/a&gt; server.
ZEO uses a client/server architecture. The server process opens one or
more storages (in practice, always a FileStorage &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f5" id="id6"&gt;[6]&lt;/a&gt;) and exposes a
network API to provide access to this storage. Client processes
connect to this server and send it read and write requests. The server
mediates access to the underlying storage for the clients.&lt;/p&gt;
&lt;p&gt;ZEO inherits many of the strengths and weaknesses of its underlying
storage and adds some of its own. For example, clients can be
configured with a persistent local cache for cheap access to common
objects or even read-only access when the server isn't available. But
the central ZEO process has to contend with Python's &lt;a class="reference external" href="https://speakerdeck.com/dabeaz/understanding-the-python-gil"&gt;GIL&lt;/a&gt;, which may
limit scalability, and it defaults to resolving conflicts by loading
application code into the server process, which can complicate
deployments due to the need to keep client and server processes all
running compatible code.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/zopefoundation/zc.zrs"&gt;ZRS&lt;/a&gt; &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#f6" id="id7"&gt;[7]&lt;/a&gt; is a storage wrapper implemented in Python and commonly
wrapped around a ZEO storage that provides replication of data.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="what-is-relstorage"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id14"&gt;What is RelStorage?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://relstorage.readthedocs.io"&gt;RelStorage&lt;/a&gt; is a ZODB storage engine that's meant to solve many of the
same problems as ZEO and ZRS, but taking a different approach with a
different set of tradeoffs. RelStorage uses a relational database—MySQL, PostgreSQL, Oracle, or SQLite—to provide the final
storage for object state data. It pushes the responsibility for OID
allocation, locks, transaction management and snapshot isolation, and
replication down to these systems.&lt;/p&gt;
&lt;p&gt;The next section is mostly a copy of RelStorage's own description of
its features. It makes references to ZEO and FileStorage described
above.&lt;/p&gt;
&lt;div class="section" id="features"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id15"&gt;Features&lt;/a&gt;&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It is a drop-in replacement for FileStorage and ZEO, with several
enhancements:&lt;ul&gt;
&lt;li&gt;Supports undo, packing, and object history preservation just like
FileStorage.&lt;/li&gt;
&lt;li&gt;RelStorage can be configured &lt;em&gt;not&lt;/em&gt; to keep object histories for
reduced disk space usage and improved performance.&lt;/li&gt;
&lt;li&gt;Multiple processes on a single machine can read and write a local
ZODB database using SQLite without needing to start and manage
another process (i.e., ZEO).&lt;/li&gt;
&lt;li&gt;Blobs can be stored on a shared filesystem, or (recommended) in
the relational database and only cached locally.&lt;/li&gt;
&lt;li&gt;Multiple threads in the same process share a high-performance
in-memory pickle cache to reduce the number of queries to the
RDBMS. This is similar to ZEO, and the ZEO cache trace tools are
supported.&lt;/li&gt;
&lt;li&gt;The in-memory pickle cache can be saved to disk and read when a
process starts up. This can dramatically speed up site warmup time
by eliminating a flood of RDBMS queries. Unlike ZEO, this cache
is automatically shared by all processes on the machine (no need
to configure separate client identifiers.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ideal for large, high volume sites.&lt;ul&gt;
&lt;li&gt;Multiple Python processes on multiple machines can read and write
the same ZODB database concurrently. This is similar to ZEO, but
RelStorage does not require ZEO.&lt;/li&gt;
&lt;li&gt;Supports ZODB 5's parallel commit feature: Database writers only
block each other when they would conflict (except for a small
window at the end of the twophase commit protocol when the
transaction ID is allocated; that still requires a global database
lock).&lt;/li&gt;
&lt;li&gt;According to some tests, RelStorage handles concurrency better than
the standard combination of ZEO and FileStorage.&lt;/li&gt;
&lt;li&gt;Whereas FileStorage takes longer to start as the database grows
due to an in-memory index of all objects, RelStorage starts
quickly regardless of database size.&lt;/li&gt;
&lt;li&gt;Capable of failover to replicated SQL databases.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tested integration with &lt;a class="reference external" href="http://gevent.org_"&gt;gevent&lt;/a&gt; for PostgreSQL and MySQL.&lt;/li&gt;
&lt;li&gt;There is a simple way (&lt;a class="reference external" href="https://relstorage.readthedocs.io/en/latest/zodbconvert.html_"&gt;zodbconvert&lt;/a&gt;) to (incrementally) convert
FileStorage to RelStorage and back again. You can also convert a
RelStorage instance to a different relational database. This is a
general tool that can be used to convert between any two ZODB
storage implementations.&lt;/li&gt;
&lt;li&gt;There is a simple way (&lt;a class="reference external" href="https://relstorage.readthedocs.io/en/latest/zodbpack.html_"&gt;zodbpack&lt;/a&gt;) to pack databases.&lt;/li&gt;
&lt;li&gt;Supports &lt;a class="reference external" href="https://relstorage.readthedocs.io/en/latest/zodburi.html"&gt;zodburi&lt;/a&gt; .&lt;/li&gt;
&lt;li&gt;Free, open source (ZPL 2.1)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="mvcc-and-history-free-storage"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id16"&gt;MVCC and History Free Storage&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One thing in particular I'd like to highlight is that RelStorage can
implement snapshot isolation and conflict resolution &lt;em&gt;without&lt;/em&gt;
preserving history. To do this, it relies on the RDBMS's native
implementation of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control"&gt;MVCC&lt;/a&gt;, the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Isolation_(database_systems)#Repeatable_reads"&gt;repeatable read&lt;/a&gt; isolation level, and
the read committed isolation level.&lt;/p&gt;
&lt;p&gt;When a transaction begins, a RDBMS transaction is opened on a
connection at the repeatable read (or higher) level. This connection
is used for loading data from the database. This isolation level
causes the RDBMS to establish its own snapshot view of the database
as-of that moment of time.&lt;/p&gt;
&lt;p&gt;A second connection is used to write data to the database. This
connection is in the &lt;em&gt;lower&lt;/em&gt; isolation level of simply read committed.
This level ensures that each query it makes to the database returns
the latest committed data. Objects being written are first placed in a
temporary table; they are moved to their final table (overwriting an
old revision for history free storages) only after any possible
conflicts have been found and resolved.&lt;/p&gt;
&lt;p&gt;The difference in the two connections' isolation levels matters
specifically because of conflict resolution, as does the use of a
temporary table. Recall that resolving conflicts needs three versions
of the object: the object that existed when the transaction began (the
original object), the object that is currently committed and was
changed by someone else (the committed object), and the object that
the writer would like to store (the new object). The task of the
conflict resolution is to find the delta between the original object
and the new object and apply those same changes to the committed
object. This produces a new object to store which will become the
committed object.&lt;/p&gt;
&lt;p&gt;Ignoring caches, the &lt;em&gt;only&lt;/em&gt; place that original object can come from
is that load connection at repeatable read isolation level. By
definition, any fresh connection or transaction that looked at the
database now would see the currently committed object (or something
even later)—the original object has been overwritten and that
change committed, so it's gone. RelStorage relies on the underlying
database to keep it visible to the load connection.&lt;/p&gt;
&lt;p&gt;Likewise, getting the currently committed object requires a connection
that can read the current state of the database. That's where the
second connection comes in. It can see the current data in the
database.&lt;/p&gt;
&lt;div class="section" id="q-a"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id17"&gt;Q &amp;amp; A&lt;/a&gt;&lt;/h4&gt;
&lt;!-- ATTENTION AUTHOR: Topic text must be on a single line. Watch your wrapping! --&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Why two connections? Why not put the data in the temporary table, commit, and begin a new transaction to update the current view of the database?&lt;/p&gt;
&lt;p&gt;Because that would lose access to the original object.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Why not preemptively store off all the original objects somewhere (e.g., download them or copy them to a temp table) before committing?&lt;/p&gt;
&lt;p&gt;Because ZODB uses an optimistic concurrency model. We assume
that conflicts are few and far between. If that's true, that would
be doing a bunch of extra work that we don't usually need to do.
Remember, there's no way to know if there's going to be a conflict
or not without a current view of the database.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Well then, why not just have a single shared connection for the current view of the database and use it to check for conflicts and only then save the original objects that have conflicts?&lt;/p&gt;
&lt;p&gt;Because that connection wouldn't know what objects to check for
conflicts on. Those objects are already in the database in
temporary tables that are connection specific and unreadable to a
different connection. We'd have to pass a list of object IDs back
to the database, and not all databases support array operations to
do that efficiently. Or we'd have to write to a persistent table,
which doesn't sound appealing (we'd have to arrange to delete from
it too.)&lt;/p&gt;
&lt;p&gt;Also, because RDBMS connections aren't thread-safe, that would
introduce a per-process lock into the commit process.&lt;/p&gt;
&lt;p&gt;Still, perhaps that's worth looking into more.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Couldn't a history-preserving database implement snapshot isolation just like FileStorage and use only one read committed connection?&lt;/p&gt;
&lt;p&gt;Quite possibly, yes. That could make for some moderately ugly or
inefficient SQL queries though.&lt;/p&gt;
&lt;pre class="code sql"&gt;&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;object_state&lt;/span&gt;
&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;zoid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;zoid&lt;/span&gt;
&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;tid&lt;/span&gt;
&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;a name="rest_code_3b3b230cefa14b94bc3d86499f41cb9e-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="topic"&gt;
&lt;p class="topic-title first"&gt;Why temp tables? Why not store directly to the final table?&lt;/p&gt;
&lt;p&gt;For history free databases, the final table is where we get the
data to resolve conflicts, so we can't overwrite it.&lt;/p&gt;
&lt;p&gt;For history preserving databases, we don't yet have the necessary
transaction ID we need to store to the final table. (The primary
key is (OID, TID), and the TID is a foreign key reference to
another tables as well).&lt;/p&gt;
&lt;p&gt;We could allocate the TID earlier, before storing temporary data,
but that defeats much of the benefit of ZODB 5 parallel commit.&lt;/p&gt;
&lt;p&gt;We could use a fake TID and update it in-place, but altering
primary keys tends to be expensive.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conflict-resolution"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id18"&gt;Conflict Resolution&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RelStorage supports conflict resolution. Conflict resolution is
performed in each individual process in a distributed fashion. There's
no central server that has to be updated with application code in
order to resolve conflicts. ZEO 5 supports a similar feature.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id19"&gt;Summary&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZODB is a flexible and powerful object database for Python, supporting
transactions, optimistic concurrency, and conflict resolution. It uses
a layered architecture with the definition and serialization of
individual objects handled by the &lt;tt class="docutils literal"&gt;persistent&lt;/tt&gt; library, the
generic transactional API provided by &lt;a class="reference external" href="https://transaction.readthedocs.io"&gt;transaction&lt;/a&gt;, and data storage and MVCC
semantics provided by the pluggable storage layer.&lt;/p&gt;
&lt;p&gt;ZODB comes with a storage implementation using an append-only file, as
well as an in-memory &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/master/src/ZODB/MappingStorage.py"&gt;dict-based&lt;/a&gt;
storage plus a change-tracking &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/master/src/ZODB/DemoStorage.py"&gt;demo storage&lt;/a&gt;.
These are all restricted to a single process, but ZEO allows utilizing
them from multiple processes.&lt;/p&gt;
&lt;p&gt;RelStorage is a storage layer based on a SQL database, intended to be
highly scalable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="updates"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id20"&gt;Updates&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Add additional links to more resources about included and
third-party storages.&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;ZODB may stand for "Zope Object Database," or &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/pull/137/files"&gt;it may stand for&lt;/a&gt; "Z Object Database."&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;To the storage engine, the object state data is just an
opaque sequence of bytes. In reality, the ZODB Connection
uses Python's standard &lt;a class="reference external" href="https://docs.python.org/3/library/pickle.html"&gt;pickle&lt;/a&gt; protocol
to serialize objects into bytes.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2a" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Storages also handle non-object data in the form of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Binary_large_object"&gt;BLOBs&lt;/a&gt;, each
of which is associated with an object and assigned an OID.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Actually, the layer that implements snapshot isolation on
top of an arbitrary history preserving storage is
&lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/blob/master/src/ZODB/mvccadapter.py"&gt;found in the core of ZODB&lt;/a&gt;.
This was one of the major changes in ZODB 5.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Previously "&lt;a class="reference external" href="https://github.com/zopefoundation/ZEO/commit/6770c28e46ac1c6585894d20546cd2e11978d4d5#diff-88b99bb28683bd5b7e3a204826ead112L10"&gt;Zope Enterprise Objects&lt;/a&gt;".&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Though one possibly wrapped in something like &lt;a class="reference external" href="http://www.zodb.org/en/latest/reference/storages.html#optional-layers"&gt;zlibstorage&lt;/a&gt;
to provide compression or &lt;tt class="docutils literal"&gt;cipher.encryptingstorage&lt;/tt&gt; to
provide encryption.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2019/10/intro-zodb.html#id7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;"ZODB Replicated Storage"&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>mysql</category><category>oracle</category><category>postgresql</category><category>python</category><category>relstorage</category><category>sql</category><category>zodb</category><guid>https://seecoresoftware.com/blog/2019/10/intro-zodb.html</guid><pubDate>Mon, 28 Oct 2019 18:07:52 GMT</pubDate></item><item><title>Python Argument Surprise</title><link>https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;Python function signatures are flexible, complex beasts, allowing for
positional, keyword, variable, and variable keyword arguments (and
parameters). This can be extremely useful, but sometimes the
intersection between these features can be confusing or even
surprising, especially on Python 2. What do you expect this to return?&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_3be57255b2be41e89f489d2afcf98b99-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_3be57255b2be41e89f489d2afcf98b99-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;
&lt;a name="rest_code_3be57255b2be41e89f489d2afcf98b99-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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="s1"&gt;'arg1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;a name="rest_code_3be57255b2be41e89f489d2afcf98b99-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#terminology" id="id1"&gt;Terminology&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#surprises" id="id2"&gt;Surprises&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#non-default-parameters-accept-keyword-arguments" id="id3"&gt;Non-Default Parameters Accept Keyword Arguments&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#corollary-variable-keyword-arguments-can-bind-non-default-parameters" id="id4"&gt;Corollary: Variable Keyword Arguments Can Bind Non-Default Parameters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#corollary-positional-parameters-consume-keyword-arguments" id="id5"&gt;Corollary: Positional Parameters Consume Keyword Arguments&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#default-parameters-accept-positional-arguments" id="id6"&gt;Default Parameters Accept Positional Arguments&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#corollary-variable-positional-arguments-can-bind-default-parameters" id="id7"&gt;Corollary: Variable Positional Arguments Can Bind Default Parameters&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#mixing-variable-parameters-and-keyword-arguments-will-break" id="id8"&gt;Mixing Variable Parameters and Keyword Arguments Will Break&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#functions-implemented-in-c-can-break-the-rules" id="id9"&gt;Functions Implemented In C Can Break The Rules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#python-3-improvements" id="id10"&gt;Python 3 Improvements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="terminology"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id1"&gt;Terminology&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we move on, let's take a minute to define some terms.&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;parameter&lt;/dt&gt;
&lt;dd&gt;The name of a variable listed by a function definition. These are
sometimes called "formal parameters" or "formal arguments." In
&lt;tt class="docutils literal"&gt;def foo(a, b)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;a&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;b&lt;/tt&gt; are parameters.&lt;/dd&gt;
&lt;dt&gt;argument&lt;/dt&gt;
&lt;dd&gt;The expression given to a function application (function
call). In &lt;tt class="docutils literal"&gt;foo(1, "str")&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;"str"&lt;/tt&gt; are arguments.&lt;/dd&gt;
&lt;dt&gt;function signature&lt;/dt&gt;
&lt;dd&gt;The set of parameters in a function definition. This is also known
as a "parameter list."&lt;/dd&gt;
&lt;dt&gt;binding&lt;/dt&gt;
&lt;dd&gt;The process of associating function call &lt;em&gt;arguments&lt;/em&gt; to the parameter
names given in the function's signature. In &lt;tt class="docutils literal"&gt;foo(1, "str")&lt;/tt&gt;,
the parameter &lt;tt class="docutils literal"&gt;a&lt;/tt&gt; will be assigned the value &lt;tt class="docutils literal"&gt;1&lt;/tt&gt;, and the
parameter &lt;tt class="docutils literal"&gt;b&lt;/tt&gt; will be assigned the value &lt;tt class="docutils literal"&gt;"str"&lt;/tt&gt;. This is also
called "argument filling."&lt;/dd&gt;
&lt;dt&gt;default parameter&lt;/dt&gt;
&lt;dd&gt;In a function signature, a parameter that is assigned a value.
An argument for this parameter does not have to be given in a function
application; when it is not, the default value is bound to the
parameter. In &lt;tt class="docutils literal"&gt;def foo(a, b=42)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;b=42&lt;/tt&gt; creates a default
parameter. It can also be said that &lt;tt class="docutils literal"&gt;b&lt;/tt&gt; has a default parameter
value. The function can be called as &lt;tt class="docutils literal"&gt;foo(1).&lt;/tt&gt;&lt;/dd&gt;
&lt;dt&gt;positional argument&lt;/dt&gt;
&lt;dd&gt;An argument in a function call that's given in order of the
parameters in the function signature, from left to right. In
&lt;tt class="docutils literal"&gt;foo(1, 2)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;1&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;2&lt;/tt&gt; are positional arguments that will
be bound to the parameters &lt;tt class="docutils literal"&gt;a&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;b&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;keyword argument&lt;/dt&gt;
&lt;dd&gt;An argument in a function call that's given by name, matching the
name of a parameter. In &lt;tt class="docutils literal"&gt;foo(a=1)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;a=1&lt;/tt&gt; is a keyword
argument, and the parameter &lt;tt class="docutils literal"&gt;a&lt;/tt&gt; will have the value &lt;tt class="docutils literal"&gt;1&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;variable parameters&lt;/dt&gt;
&lt;dd&gt;A function signature that contains &lt;tt class="docutils literal"&gt;*args&lt;/tt&gt; (where &lt;tt class="docutils literal"&gt;args&lt;/tt&gt; is an
arbitrary identifier) accepts an arbitrary number of unnamed
arguments in addition to any explicit parameters. Extra
parameters are bound to &lt;tt class="docutils literal"&gt;args&lt;/tt&gt; as a list. &lt;tt class="docutils literal"&gt;def
foo(a, b, *args)&lt;/tt&gt; creates a function that has variable
parameters, and &lt;tt class="docutils literal"&gt;foo(1, 2)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;foo(1, 2, 3)&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;foo(1, 2, 3,
4)&lt;/tt&gt; are all valid ways to call it. This is commonly called
"varargs," for "variable arguments" (even though it is a parameter
definition).&lt;/dd&gt;
&lt;dt&gt;variable positional arguments&lt;/dt&gt;
&lt;dd&gt;Passing an arbitrary (usually unknown from the function call itself)
number of arguments to a function by unpacking a sequence.
Variable arguments can be given to a function whether or not it
accepts variable parameters (if it doesn't, the number of variable
arguments must match the number of parameters). This is done using
the &lt;tt class="docutils literal"&gt;*&lt;/tt&gt; syntax: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;foo(*(1,&lt;/span&gt; 2))&lt;/tt&gt; is the same as writing &lt;tt class="docutils literal"&gt;foo(1,
2)&lt;/tt&gt;, but more often the arguments are created dynamically. For
example, &lt;tt class="docutils literal"&gt;args = (1, 2) if full_moon else (3, 4); &lt;span class="pre"&gt;foo(*args)&lt;/span&gt;&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;variable keyword parameters&lt;/dt&gt;
&lt;dd&gt;A function signature that contains &lt;tt class="docutils literal"&gt;**kwargs&lt;/tt&gt; (where &lt;tt class="docutils literal"&gt;kwargs&lt;/tt&gt;
is an arbitrary identifier) accepts an arbitrary number of keyword
arguments in addition to any explicit parameters (with or without
default values). The definition &lt;tt class="docutils literal"&gt;def foo(a, b, **kwargs)&lt;/tt&gt;
creates a function with a variable keyword parameter. It can be
called like &lt;tt class="docutils literal"&gt;foo(1, 2)&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;foo(1, 2, c=42, d=420)&lt;/tt&gt;.&lt;/dd&gt;
&lt;dt&gt;variable positional arguments&lt;/dt&gt;
&lt;dd&gt;Similar to &lt;em&gt;variable positional arguments&lt;/em&gt;, but using keyword
arguments. The syntax is &lt;tt class="docutils literal"&gt;**&lt;/tt&gt;, and the object to be unpacked
must be a mapping; extra arguments are placed in a mapping bound
to the parameter identifier. A simple example is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;foo(**{'b':"str",&lt;/span&gt; &lt;span class="pre"&gt;'a':1})&lt;/span&gt;&lt;/tt&gt;.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Some language communities are fairly strict about the usage of these
terms, but the Python community is often fairly informal. This is
especially true when it comes to the distinction between &lt;em&gt;parameters&lt;/em&gt;
and &lt;em&gt;arguments&lt;/em&gt; (despite it being a &lt;a class="reference external" href="https://docs.python.org/3/faq/programming.html#faq-argument-vs-parameter"&gt;FAQ&lt;/a&gt;)
which helps lead to some of the surprises we discuss below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="surprises"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id2"&gt;Surprises&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On to the surprises. These will all come from the intersection of the
various terms defined above. Not all of these will surprise everyone,
but &lt;em&gt;I&lt;/em&gt; would be surprised if most people didn't discover at least one
mildly surprising thing.&lt;/p&gt;
&lt;div class="section" id="non-default-parameters-accept-keyword-arguments"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id3"&gt;Non-Default Parameters Accept Keyword Arguments&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Any parameter can be called using a keyword argument, whether or not
it has a default parameter value:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 2, 42)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 'b', 42)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_19a427bdad6e493990f962fb58acdf01-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;(3, 2, 1)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This is surprising because sometimes parameters with a default value
are referred to as "keyword parameters" or "keyword arguments,"
suggesting that only they can be called using a keyword argument. In
reality, the parameter just has a default value. It's the function
call site that determines whether to use a keyword argument or not.&lt;/p&gt;
&lt;p&gt;One consequence of this: the parameter names of public functions, even
if they don't have a default, are part of the public signature of the
function. If you distribute a library, people can and will call
functions using keyword arguments for parameters you didn't expect
them to. Changing parameter names can thus break backwards
compatibility. (Below we'll see how Python 3 can help with this.)&lt;/p&gt;
&lt;div class="section" id="corollary-variable-keyword-arguments-can-bind-non-default-parameters"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id4"&gt;Corollary: Variable Keyword Arguments Can Bind Non-Default Parameters&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If we introduce variable keyword arguments, we see that this behaviour
is consistent:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_4e787363cf5742f58a52a2779af03925-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'akw'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'bkw'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a name="rest_code_4e787363cf5742f58a52a2779af03925-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_4e787363cf5742f58a52a2779af03925-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;('akw', 'bkw', 42)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="corollary-positional-parameters-consume-keyword-arguments"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id5"&gt;Corollary: Positional Parameters Consume Keyword Arguments&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Knowing what we know now, we can answer the teaser from the beginning
of the article:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_16f26c05f3184c15a5aa4f2bd465c3d3-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_16f26c05f3184c15a5aa4f2bd465c3d3-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;
&lt;a name="rest_code_16f26c05f3184c15a5aa4f2bd465c3d3-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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="s1"&gt;'arg1'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;a name="rest_code_16f26c05f3184c15a5aa4f2bd465c3d3-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;(42, {})&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The named parameter &lt;tt class="docutils literal"&gt;arg1&lt;/tt&gt;, even when passed in a variable keyword
argument, is still bound by name. There are no extra arguments to
place in &lt;tt class="docutils literal"&gt;kwargs&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="default-parameters-accept-positional-arguments"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id6"&gt;Default Parameters Accept Positional Arguments&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Any parameter can be called using a positional argument, whether or
not it has a default parameter value:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_cbdd379f717e4b23a415692c15c72e41-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_cbdd379f717e4b23a415692c15c72e41-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cbdd379f717e4b23a415692c15c72e41-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cbdd379f717e4b23a415692c15c72e41-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;('a', 'b', 'c')&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This is the inverse of the previous surprise. It may be surprising for
the same reason, the conflation of keyword arguments and default
parameter values.&lt;/p&gt;
&lt;p&gt;Of course, convention often dictates that default parameters are
passed using keyword arguments, but as you can see, that's not a
requirement of the language.&lt;/p&gt;
&lt;div class="section" id="corollary-variable-positional-arguments-can-bind-default-parameters"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id7"&gt;Corollary: Variable Positional Arguments Can Bind Default Parameters&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Introducing variable positional arguments shows consistent behaviour:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_7d0dc7251d7e4c2db3f1f36e11a7b42e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pos_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'apos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bpos'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_7d0dc7251d7e4c2db3f1f36e11a7b42e-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pos_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_7d0dc7251d7e4c2db3f1f36e11a7b42e-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;('apos', 'bpos', 3)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="mixing-variable-parameters-and-keyword-arguments-will-break"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id8"&gt;Mixing Variable Parameters and Keyword Arguments Will Break&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Suppose we'd like to define some parameters with default values
(expecting them to be passed as keyword arguments by convention), and
then also allow for some extra arguments to be passed:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_1e62b1e65cce42179359ecf289396e6e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&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;a name="rest_code_1e62b1e65cce42179359ecf289396e6e-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&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;/pre&gt;&lt;p&gt;The definition works. Now lets call it in some common patterns:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;('a', 'b', ())&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;('a', 'b', ('c',))&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;('a', 1, ())&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-9"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-10"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_c6c29c0bab414b5eb3314605951c918f-11"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;test() got multiple values for argument 'b'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;As long as we don't mix keyword and variable (extra) arguments,
everything works out. But as soon as we mix the two, the variable
positional arguments are bound first, and then we have a duplicate
keyword argument left over for &lt;tt class="docutils literal"&gt;b&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This is a common enough source of errors that, as we'll see below,
Python 3 added some extra help for it, and linters &lt;a class="reference external" href="https://github.com/PyCQA/pylint/issues/1835"&gt;warn about it&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="functions-implemented-in-c-can-break-the-rules"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id9"&gt;Functions Implemented In C Can Break The Rules&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We generally expect to be able to call functions with keyword
arguments, &lt;em&gt;especially&lt;/em&gt; when the corresponding parameters have default
values, and we expect that the order of keyword arguments doesn't
matter. But if the function is not implemented in Python, and instead
is a built-in function implemented in C, that may not be the case.
Let's look at the built-in function &lt;tt class="docutils literal"&gt;math.tan&lt;/tt&gt;. On Python 3, if we ask
for the function signature, we get something like this:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Help on built-in function tan in module math:&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;BLANKLINE&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;    tan(x)&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;BLANKLINE&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Return the tangent of x (measured in radians).&lt;/span&gt;
&lt;a name="rest_code_7157a21013c443d98968ae88dbceea18-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;BLANKLINE&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;That sure looks like a parameter with a name, so we
expect to be able to call it with a keyword argument:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_af4b7c551e214997bd1864f8549531be-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_af4b7c551e214997bd1864f8549531be-2"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_af4b7c551e214997bd1864f8549531be-3"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_af4b7c551e214997bd1864f8549531be-4"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;tan() takes no keyword arguments&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This is due to how C functions bind Python arguments into C variables,
using functions like &lt;a class="reference external" href="https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple"&gt;PyArg_ParseTuple&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In newer versions of Python, this is indicated with a trailing &lt;tt class="docutils literal"&gt;/&lt;/tt&gt;
in the function signature, showing that the preceding arguments
are &lt;a class="reference external" href="https://docs.python.org/3/glossary.html#positional-only-parameter"&gt;positional only paramaters&lt;/a&gt;.
(Note that Python has no syntax for this.)&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_360adb56a75a4d1b95fd99e8dc92776c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_360adb56a75a4d1b95fd99e8dc92776c-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;Help on built-in function abs in module builtins:&lt;/span&gt;
&lt;a name="rest_code_360adb56a75a4d1b95fd99e8dc92776c-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;BLANKLINE&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_360adb56a75a4d1b95fd99e8dc92776c-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;abs(x, /)&lt;/span&gt;
&lt;a name="rest_code_360adb56a75a4d1b95fd99e8dc92776c-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Return the absolute value of the argument.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="python-3-improvements"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html#id10"&gt;Python 3 Improvements&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Python 3 offers ways to reduce some of these surprising
characteristics. (For backwards compatibility, it doesn't actually
eliminate any of them.)&lt;/p&gt;
&lt;p&gt;We've already seen that functions implemented in C can use new syntax
in their function signatures to signify positional-only arguments.
Plus, more C functions can accept keyword arguments for any arbitrary
parameter thanks to new functions and the use of tools like &lt;a class="reference external" href="https://docs.python.org/3/howto/clinic.html"&gt;Argument
Clinic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The most important improvements, though, are available to Python
functions and are outlined in the confusingly named &lt;a class="reference external" href="http://www.python.org/dev/peps/pep-3102"&gt;PEP 3102&lt;/a&gt;:
Keyword-Only Arguments.&lt;/p&gt;
&lt;p&gt;With this PEP, functions are allowed to define parameters that can
only be filled by keyword arguments. In addition, this allows
functions to accept both variable arguments and keyword arguments
without raising &lt;tt class="docutils literal"&gt;TypeError&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;This in done by simply moving the variable positional parameter
&lt;em&gt;before&lt;/em&gt; any parameters that should only be allowed by keyword:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&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;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&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;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 42, (2, 3))&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 'b', (2, 3))&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-8"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-9"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-10"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;test() got an unexpected keyword argument 'c'&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-12"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-13"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_52d0f65f170a4bcc82c06529a9dfc6ee-14"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;test() missing 1 required positional argument: 'a'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;What if you don't want to allow arbitrary unnamed arguments? In that
case, simply omit the variable argument parameter name:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_264757521b704216b8b96887a6563df8-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&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;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_264757521b704216b8b96887a6563df8-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_264757521b704216b8b96887a6563df8-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_264757521b704216b8b96887a6563df8-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 'b')&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Trying to pass extra arguments will fail:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_3c98d97969fe40c5bc9563cc671c403b-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_3c98d97969fe40c5bc9563cc671c403b-2"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_3c98d97969fe40c5bc9563cc671c403b-3"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_3c98d97969fe40c5bc9563cc671c403b-4"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;test() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Finally, what if you want to &lt;em&gt;require&lt;/em&gt; certain parameters to be passed
by name? In that case, you can simply leave off the default value for
the keyword-only parameters:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&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;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-4"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-5"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-6"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;test() missing 1 required keyword-only argument: 'b'&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_67dfb9f6433440edb511981a1b6f9070-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 'b')&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The above examples all produce &lt;tt class="docutils literal"&gt;SyntaxError&lt;/tt&gt; on Python 2. Much of
the functionality can be achieved on Python 2 using variable arguments
and variable keyword arguments and manual argument binding, but that's
slower and uglier than what's available on Python 3. Lets look at an
example of implementing the first function from this section in Python
2:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s2"&gt;"test(a, *args, b=42) -&amp;gt; tuple"&lt;/span&gt; &lt;span class="c1"&gt;# docstring signature for Sphinx&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="c1"&gt;# This raises an IndexError instead of a TypeError if 'a'&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="c1"&gt;# is missing; that's easy to fix, but it's a programmer error&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="n"&gt;a&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="n"&gt;args&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;       &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Got extra keyword args &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&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;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 42, (2, 3))&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-13"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1, 'b', (2, 3))&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-15"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-16"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-17"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-18"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;Got extra keyword args ['c']&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-19"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-20"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-21"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_345ca03f1e0d44d0ae3fd7472b61ba47-22"&gt;&lt;/a&gt;&lt;span class="gr"&gt;IndexError&lt;/span&gt;: &lt;span class="n"&gt;tuple index out of range&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</description><category>2</category><category>3</category><category>argument</category><category>python</category><category>surprise</category><guid>https://seecoresoftware.com/blog/2018/11/python-argument-surprise.html</guid><pubDate>Fri, 23 Nov 2018 15:11:07 GMT</pubDate></item><item><title>Getting Started With Python inside PostgreSQL</title><link>https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;figure&gt;&lt;img src="https://www.postgresql.org/media/img/about/press/elephant.png"&gt;&lt;/figure&gt; &lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt; uses the tagline "the world's most advanced open source
relational database." For PostgreSQL, part of being "advanced" means
supporting &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/xplang.html"&gt;multiple server-side procedural languages&lt;/a&gt;, both built-in
and &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/external-pl.html"&gt;provided by third parties&lt;/a&gt;. Luckily for us
here at the blog, one of the built-in languages is Python. Unluckily,
it's not completely obvious how to get started &lt;em&gt;using&lt;/em&gt; Python inside
PostgreSQL. This post will provide a short walkthrough demonstrating
how to do that. It's written for macOS, but many of the steps are generic.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#server-programming-basics" id="id1"&gt;Server Programming Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#installing-postgresql" id="id2"&gt;Installing PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#creating-users" id="id3"&gt;Creating Users&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#creating-languages" id="id4"&gt;Creating Languages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#untrusted-languages" id="id5"&gt;Untrusted Languages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#more" id="id6"&gt;More&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="server-programming-basics"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id1"&gt;Server Programming Basics&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Relational database servers often support user-defined functions
and/or procedures. In the best implementations, they can be used
whenever a built-in function or procedure can be used. For example,
they might be used in a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SQL"&gt;SQL&lt;/a&gt; &lt;tt class="docutils literal"&gt;SELECT&lt;/tt&gt; statement to compute values
for a column in a result set, or they might be executed automatically
as a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Database_trigger"&gt;trigger&lt;/a&gt; when data in a table changes, or they might even be
called directly by a client to perform some complex action.&lt;/p&gt;
&lt;p&gt;The database servers that support user-defined functions and
procedures tend to offer a custom language to write them in. These
languages tend to be somewhat similar to &lt;a class="reference external" href="https://en.wikipedia.org/wiki/SQL"&gt;SQL&lt;/a&gt;. While that can ease
writing queries and updates, it can also lead to baroque code when the
logic gets more complicated.&lt;/p&gt;
&lt;p&gt;In PostgreSQL, that SQL-derived language is called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/PL/pgSQL"&gt;PL/pgSQL&lt;/a&gt;, in
Oracle database it's called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/PL/SQL"&gt;PL/SQL&lt;/a&gt;, and in Microsoft SQL server,
it's called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Transact-SQL"&gt;T-SQL&lt;/a&gt;. MySQL doesn't seem to give a name to its
dialect, simply referring to SQL &lt;a class="reference external" href="https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-compound-statements.html"&gt;Compound-Statements&lt;/a&gt;. All of these
languages are very different from one another (although PL/pgSQL and
PL/SQL share some similarities).&lt;/p&gt;
&lt;p&gt;PostgreSQL goes beyond that, using its extension mechanism to support
multiple server-side procedural languages. These include:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/pltcl.html"&gt;TCL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/plperl.html"&gt;Perl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/plpython.html"&gt;Python 2 and 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://tada.github.io/pljava/"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/pllua/pllua"&gt;Lua&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://www.joeconway.com/plr.html"&gt;R&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/petere/plsh"&gt;Unix shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/plv8/plv8"&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="installing-postgresql"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id2"&gt;Installing PostgreSQL&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step to working with any of those procedural languages is to
install PostgreSQL. Because all of these languages are extensions, and
thus optional, we need to also compile the extensions we wish to use.
Here we'll be using &lt;a class="reference external" href="https://www.macports.org"&gt;MacPorts&lt;/a&gt; to install PostgreSQL 10 with support
for Python:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo port install postgresql10 +python
&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Computing dependencies for postgresql10&lt;/span&gt;
&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Fetching archive for postgresql10&lt;/span&gt;
&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Some of the ports you installed have notes:&lt;/span&gt;
&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;  postgresql10 has the following notes:&lt;/span&gt;
&lt;a name="rest_code_43ed05b0234144679d99fe2a142645a2-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;    To use the postgresql server, install the postgresql10-server port&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We want to use the server, not just the client, so we follow the
instructions to install the server:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo port install postgresql10-server
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Computing dependencies for postgresql10-server&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Fetching archive for postgresql10-server&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;...&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-5"&gt;&lt;/a&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;---&amp;gt;  Some of the ports you installed have notes:&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;  postgresql10-server has the following notes:&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;    To create a database instance, after install do&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;      sudo mkdir -p /opt/local/var/db/postgresql10/defaultdb&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;      sudo chown postgres:postgres /opt/local/var/db/postgresql10/defaultdb&lt;/span&gt;
&lt;a name="rest_code_8f2bf9e4f3014552b6346099adf90bec-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;      sudo su postgres -c 'cd /opt/local/var/db/postgresql10 &amp;amp;&amp;amp; /opt/local/lib/postgresql10/bin/initdb -D /opt/local/var/db/postgresql10/defaultdb'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We are prompted to create the initial database, so we follow the
instructions there too:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo mkdir -p /opt/local/var/db/postgresql10/defaultdb
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo chown postgres:postgres /opt/local/var/db/postgresql10/defaultdb
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo su postgres -c &lt;span class="s1"&gt;'cd /opt/local/var/db/postgresql10 &amp;amp;&amp;amp; /opt/local/lib/postgresql10/bin/initdb -D /opt/local/var/db/postgresql10/defaultdb'&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;The files belonging to this database system will be owned by user "postgres".&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;This user must also own the server process.&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;The database cluster will be initialized with locale "en_US.UTF-8".&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;The default database encoding has accordingly been set to "UTF8".&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;The default text search configuration will be set to "english".&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;...&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;WARNING: enabling "trust" authentication for local connections&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;You can change this by editing pg_hba.conf or using the option -A, or&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;--auth-local and --auth-host, the next time you run initdb.&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-15"&gt;&lt;/a&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;Success. You can now start the database server using:&lt;/span&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-17"&gt;&lt;/a&gt;
&lt;a name="rest_code_7f996e9cb2594dde977e1d61ed31e51e-18"&gt;&lt;/a&gt;&lt;span class="go"&gt;     /opt/local/lib/postgresql10/bin/pg_ctl -D /opt/local/var/db/postgresql10/defaultdb -l logfile start&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The instructions at the end for starting the database server will
spin it up in the background and write logging information to the file
&lt;tt class="docutils literal"&gt;logfile&lt;/tt&gt;. For the purposes of debugging and understanding, I prefer
to run the server in the foreground, like so:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo -u postgres /opt/local/lib/postgresql10/bin/postgres -D /opt/local/var/db/postgresql10/defaultdb/
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.363 CDT [18023] LOG:  listening on IPv6 address "::1", port 5432&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.364 CDT [18023] LOG:  listening on IPv6 address "fe80::1%lo0", port 5432&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.364 CDT [18023] LOG:  listening on IPv4 address "127.0.0.1", port 5432&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.364 CDT [18023] LOG:  listening on Unix socket "/tmp/.s.PGSQL.5432"&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.381 CDT [18024] LOG:  database system was shut down at 2018-09-21 07:46:28 CDT&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-09-21 07:48:14.384 CDT [18023] LOG:  database system is ready to accept connections&lt;/span&gt;
&lt;a name="rest_code_ca1988279f6d4ed8b7d387c90195ad08-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We now leave that program running in its own terminal window where we
can watch its output as we proceed. Everything else from now on will
be done in a new terminal window.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="creating-users"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id3"&gt;Creating Users&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that the server is running, we're ready to connect a client and
start experimenting. We'll be using the stock &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/app-psql.html"&gt;psql&lt;/a&gt; interactive
terminal that comes with PostgreSQL. MacPorts installs that as
&lt;tt class="docutils literal"&gt;/opt/local/bin/psql10&lt;/tt&gt;. (&lt;a class="reference external" href="https://www.pgcli.com"&gt;pgcli&lt;/a&gt; is an excellent alternative shell.)&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_a2a75043c30d4cd3a6839b477eda51f1-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; psql10
&lt;a name="rest_code_a2a75043c30d4cd3a6839b477eda51f1-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10: FATAL:  role "jmadden" does not exist&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;When we ran &lt;tt class="docutils literal"&gt;initdb&lt;/tt&gt;, we were warned that trust authentication would
be used for local connections. What this means is that our local macOS
user name would be used to connect to the server. My local user name
is 'jmadden', and there is no matching user on the server (users and
roles in PostgreSQL are essentially interchangeable), so we need to
create one. PostgreSQL ships a &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/app-createuser.html"&gt;createuser&lt;/a&gt; command for this purpose,
but it's more instructive to look under the hood to see what this does
by actually issuing the commands ourself.&lt;/p&gt;
&lt;p&gt;We do this by connecting as the only user that's pre-existing and can login, the
&lt;tt class="docutils literal"&gt;postgres&lt;/tt&gt; super-user, and creating the role:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo -u postgres psql10
&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10 (10.5)&lt;/span&gt;
&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Type "help" for help.&lt;/span&gt;
&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="n"&gt;jmadden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_74cae95bb9534d8f92c9f348c1e71819-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;CREATE ROLE&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We're not done yet, we need to grant login rights to that role:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="kp"&gt;\du&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;                                List of roles&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Role name |                         Attributes                         | Member of&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;-----------+------------------------------------------------------------+-----------&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;jmadden   | Cannot login                                               | {}&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="n"&gt;jmadden&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;createdb&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="n"&gt;createrole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;ALTER ROLE&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-10"&gt;&lt;/a&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="kp"&gt;\du&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;                                List of roles&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;Role name |                         Attributes                         | Member of&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;-----------+------------------------------------------------------------+-----------&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-15"&gt;&lt;/a&gt;&lt;span class="go"&gt;jmadden   | Create role, Create DB                                     | {}&lt;/span&gt;
&lt;a name="rest_code_cd3c467c712a4df08697fb6c3e379a66-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;In &lt;tt class="docutils literal"&gt;psql&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;\du&lt;/tt&gt; is a shortcut command for "display users". In
PostgreSQL, almost all information about database objects is kept in
system tables, where it can be read and manipulated with SQL. In this
case, the table we're updating is the &lt;tt class="docutils literal"&gt;pg_roles&lt;/tt&gt; table:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;      rolname        | rolsuper | rolinherit | rolcreaterole | rolcreatedb | rolcanlogin | rolreplication | rolconnlimit | rolpassword | rolvaliduntil | rolbypassrls | rolconfig |  oid&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;----------------------+----------+------------+---------------+-------------+-------------+----------------+--------------+-------------+---------------+--------------+-----------+-------&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;pg_signal_backend    | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           |  4200&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;postgres             | t        | t          | t             | t           | t           | t              |           -1 | ********    |               | t            |           |    10&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;pg_read_all_stats    | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           |  3375&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;pg_monitor           | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           |  3373&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;jmadden              | f        | t          | t             | t           | t           | f              |           -1 | ********    |               | f            |           | 16384&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;pg_read_all_settings | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           |  3374&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;pg_stat_scan_tables  | f        | t          | f             | f           | f           | f              |           -1 | ********    |               | f            |           |  3377&lt;/span&gt;
&lt;a name="rest_code_b81ee1ae2e314f3986a2a83dc82ffd60-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;(7 rows)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now that we can login, surely we can connect:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_3255be6e55444ad5a7824ca1705d6a8f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; psql10
&lt;a name="rest_code_3255be6e55444ad5a7824ca1705d6a8f-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10: FATAL:  database "jmadden" does not exist&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Nope! By default, PostgreSQL will connect our session to a database
matching our user name. That database doesn't exist yet. It's a good
idea to create one to hold our experiments in anyway, as opposed to
using one of the existing databases (to which we probably don't have
access rights anyway). We'll use the &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/app-createdb.html"&gt;createdb&lt;/a&gt; command to create this
database and then try to login again:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; /opt/local/lib/postgresql10/bin/createdb jmadden
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; psql10
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10 (10.5)&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;Type "help" for help.&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-5"&gt;&lt;/a&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;jmadden=&amp;gt; \l&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;                               List of databases&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;-----------+----------+----------+-------------+-------------+-----------------------&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-10"&gt;&lt;/a&gt;&lt;span class="go"&gt; jmadden   | jmadden  | UTF8     | en_US.UTF-8 | en_US.UTF-8 |&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-11"&gt;&lt;/a&gt;&lt;span class="go"&gt; postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-12"&gt;&lt;/a&gt;&lt;span class="go"&gt; template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;           |          |          |             |             | postgres=CTc/postgres&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-14"&gt;&lt;/a&gt;&lt;span class="go"&gt; template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-15"&gt;&lt;/a&gt;&lt;span class="go"&gt;           |          |          |             |             | postgres=CTc/postgres&lt;/span&gt;
&lt;a name="rest_code_92e790f2bac54b91a879c327f38f8b99-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;(4 rows)&lt;/span&gt;
&lt;/pre&gt;&lt;div class="admonition tip"&gt;
&lt;p class="first admonition-title"&gt;Tip&lt;/p&gt;
&lt;p class="last"&gt;Notice that the psql prompt is different for regular users and the
superuser; regular users have &lt;tt class="docutils literal"&gt;=&amp;gt;&lt;/tt&gt; in the prompt, while the super user
has &lt;tt class="docutils literal"&gt;=#&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="creating-languages"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id4"&gt;Creating Languages&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fantastic! Now we're logged in to our own database. Let's create the
first &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/plpython-funcs.html"&gt;example Python function&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;pymax&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-2"&gt;&lt;/a&gt;          &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-3"&gt;&lt;/a&gt;          &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="s"&gt;$$&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-4"&gt;&lt;/a&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-5"&gt;&lt;/a&gt;                 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-6"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-7"&gt;&lt;/a&gt;          &lt;span class="s"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpythonu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-8"&gt;&lt;/a&gt;&lt;span class="gs"&gt;ERROR:&lt;/span&gt;&lt;span class="gr"&gt;  language "plpythonu" does not exist&lt;/span&gt;
&lt;a name="rest_code_22fe33fd838f4ffe902eafc0158d846e-9"&gt;&lt;/a&gt;&lt;span class="gs"&gt;HINT:&lt;/span&gt;&lt;span class="gr"&gt;  Use CREATE LANGUAGE to load the language into the database.&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Well that didn't work. If we back up to the &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/plpython.html"&gt;introduction to
PL/Python&lt;/a&gt;, it says we need to create the extension first (the "PL"
means "procedural language"; we'll see what the "u" means in a little
bit):&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_93c60d07002e4210b6afd40f7ddacf12-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;extension&lt;/span&gt; &lt;span class="n"&gt;plpythonu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_93c60d07002e4210b6afd40f7ddacf12-2"&gt;&lt;/a&gt;&lt;span class="gs"&gt;ERROR:&lt;/span&gt;&lt;span class="gr"&gt;  permission denied to create extension "plpythonu"&lt;/span&gt;
&lt;a name="rest_code_93c60d07002e4210b6afd40f7ddacf12-3"&gt;&lt;/a&gt;&lt;span class="gs"&gt;HINT:&lt;/span&gt;&lt;span class="gr"&gt;  Must be superuser to create this extension.&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Hmm, ok, I suppose that makes sense. Lets use our superuser login to
create the extension:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo -u postgres psql10
&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10 (10.5)&lt;/span&gt;
&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Type "help" for help.&lt;/span&gt;
&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;postgres=# create extension plpythonu;&lt;/span&gt;
&lt;a name="rest_code_e5ef6d5e6acf46b08a663ccfd64eeb17-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;CREATE EXTENSION&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Ok, extension created. Now lets go back to our own database and create
that example function:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;pymax&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-2"&gt;&lt;/a&gt;          &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-3"&gt;&lt;/a&gt;          &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="s"&gt;$$&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-4"&gt;&lt;/a&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-5"&gt;&lt;/a&gt;                 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-6"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-7"&gt;&lt;/a&gt;          &lt;span class="s"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpythonu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-8"&gt;&lt;/a&gt;&lt;span class="gs"&gt;ERROR:&lt;/span&gt;&lt;span class="gr"&gt;  language "plpythonu" does not exist&lt;/span&gt;
&lt;a name="rest_code_6165e4b1da5d481588e2ffa510e3fd14-9"&gt;&lt;/a&gt;&lt;span class="gs"&gt;HINT:&lt;/span&gt;&lt;span class="gr"&gt;  Use CREATE LANGUAGE to load the language into the database.&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Same error! Argh!&lt;/p&gt;
&lt;p&gt;After examining the documentation a bit, it turns out that extensions
are local to particular databases. So we need to use the 'jmadden'
database as the superuser 'postgres' to create the extension in that
database:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo -u postgres psql10 jmadden
&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;psql10 (10.5)&lt;/span&gt;
&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Type "help" for help.&lt;/span&gt;
&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;jmadden=# create extension plpythonu;&lt;/span&gt;
&lt;a name="rest_code_79f6d54318e74846b858cf1c97e6e347-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;CREATE EXTENSION&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="untrusted-languages"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id5"&gt;Untrusted Languages&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Surely now we can create that simple example function:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;pymax&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-2"&gt;&lt;/a&gt;          &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-3"&gt;&lt;/a&gt;          &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="s"&gt;$$&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-4"&gt;&lt;/a&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-5"&gt;&lt;/a&gt;                 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-6"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-7"&gt;&lt;/a&gt;          &lt;span class="s"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpythonu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_5fb949aa098f44478bec9ac7f8cd965c-8"&gt;&lt;/a&gt;&lt;span class="gs"&gt;ERROR:&lt;/span&gt;&lt;span class="gr"&gt;  permission denied for language plpythonu&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;At least we got a different error this time.&lt;/p&gt;
&lt;p&gt;It turns out that the "u" in "plpythonu" means that the language is
"untrusted." I'll quote &lt;a class="reference external" href="https://www.postgresql.org/docs/10/static/plpython.html"&gt;the docs&lt;/a&gt; for what
that means:&lt;/p&gt;
&lt;blockquote&gt;
PL/Python is only available as an “untrusted” language, meaning it
does not offer any way of restricting what users can do in it and
is therefore named plpythonu. The writer of a function in
untrusted PL/Python must take care that the function cannot be
used to do anything unwanted, since it will be able to do anything
that could be done by a user logged in as the database
administrator. Only superusers can create functions in untrusted
languages such as plpythonu.&lt;/blockquote&gt;
&lt;p&gt;The safest way to work with untrusted languages is to only
&lt;em&gt;temporarily&lt;/em&gt; escalate your privileges by using a superuser role to
create "safe" functions, as outlined in this &lt;a class="reference external" href="https://dba.stackexchange.com/questions/37336/cannot-create-function-in-plpython3u-permission-denied"&gt;StackExchange post&lt;/a&gt;.
But that's no fun. Since we're only experimenting on our own machine,
and we don't &lt;em&gt;want&lt;/em&gt; to have superuser priviliges if we can avoid it
(to limit the risks of accidentally damaging our database), we have
another option: we can mark the language as trusted.&lt;/p&gt;
&lt;p&gt;To do this, we'll need our superuser shell again:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_language&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;  lanname  | lanowner | lanispl | lanpltrusted | lanplcallfoid | laninline | lanvalidator | lanacl&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;-----------+----------+---------+--------------+---------------+-----------+--------------+--------&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-4"&gt;&lt;/a&gt;&lt;span class="go"&gt; internal  |       10 | f       | f            |             0 |         0 |         2246 |&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-5"&gt;&lt;/a&gt;&lt;span class="go"&gt; c         |       10 | f       | f            |             0 |         0 |         2247 |&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-6"&gt;&lt;/a&gt;&lt;span class="go"&gt; sql       |       10 | f       | t            |             0 |         0 |         2248 |&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-7"&gt;&lt;/a&gt;&lt;span class="go"&gt; plpgsql   |       10 | t       | t            |         12545 |     12546 |        12547 |&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-8"&gt;&lt;/a&gt;&lt;span class="go"&gt; plpythonu |       10 | t       | f            |         16416 |     16417 |        16418 |&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;(5 rows)&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-10"&gt;&lt;/a&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;postgres=#&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;pg_language&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;lanpltrusted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;lanname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'plpythonu'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_aaa0a97bb3284f22abfe0a83cdc52d84-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;UPDATE 1&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now our regular user can create and execute this function:&lt;/p&gt;
&lt;pre class="code psql"&gt;&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;pymax&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-2"&gt;&lt;/a&gt;          &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-3"&gt;&lt;/a&gt;          &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="s"&gt;$$&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-4"&gt;&lt;/a&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-5"&gt;&lt;/a&gt;                 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-6"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-7"&gt;&lt;/a&gt;          &lt;span class="s"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpythonu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;CREATE FUNCTION&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;jmadden=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;pymax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-11"&gt;&lt;/a&gt;&lt;span class="go"&gt; pymax&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;-------&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;   2&lt;/span&gt;
&lt;a name="rest_code_680b27190d484ab0b70dfa85eaa72daf-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;(1 row)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="more"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html#id6"&gt;More&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PL/Python can do much more than just compare integers, of course. It
can query the database, processing rows in batches or iteratively. It
can access and produce PostgreSQL "compound types." It can be used for
row or statement level triggers. That's all beyond the scope of
this post, but you can read more about it in the PostgreSQL
documentation.&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>postgresql</category><category>python</category><category>sql</category><guid>https://seecoresoftware.com/blog/2018/09/getting-started-with-pgsql-plpythonu.html</guid><pubDate>Fri, 21 Sep 2018 15:11:07 GMT</pubDate></item><item><title>CPython vs PyPy Memory Usage</title><link>https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;figure&gt;&lt;img src="https://seecoresoftware.com/blog/2018/08/oil-1183699_1920.jpg"&gt;&lt;/figure&gt; &lt;div&gt;&lt;p&gt;If you have lots of "small" objects in a Python program (objects which
have few instance attributes), you may find that the object overhead
starts to become considerable. The common wisdom says that to reduce this in
&lt;a class="reference external" href="https://github.com/python/cpython"&gt;CPython&lt;/a&gt; you need to re-define the classes to use &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;, eliminating
the attribute dictionary. But this comes with the downsides of
limiting flexibility and eliminating the use of class defaults. Would
it surprise you to learn that &lt;a class="reference external" href="http://pypy.org"&gt;PyPy&lt;/a&gt; can significantly, and without
any effort by the programmer, reduce that overhead automatically?&lt;/p&gt;
&lt;p&gt;Let's take a look.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#what-is-being-measured" id="id9"&gt;What Is Being Measured&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#what-is-not-being-measured" id="id10"&gt;What Is Not Being Measured&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#object-internals" id="id11"&gt;Object Internals&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#objects-with-dictionaries" id="id12"&gt;Objects with Dictionaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#objects-with-slots" id="id13"&gt;Objects With Slots&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#variable-storage" id="id14"&gt;Variable Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#how-pypy-does-better" id="id15"&gt;How PyPy Does Better&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Contrary to advice, instead of &lt;a class="reference external" href="https://www.youtube.com/watch?v=k33ZQ4I4p24"&gt;starting at the very beginning&lt;/a&gt;,
we'll jump right to the end. The following graph shows the peak memory
usage of the example program we'll be talking about in this post
across seven different Python implementations: PyPy2 v6.0, PyPy3 v6.0,
CPython 2.7.15, 3.4.9, 3.5.6, 3.6.6, and 3.7.0 &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f1" id="id1"&gt;[1]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For regular objects ("Point3D"), PyPy needs less than 700MB to create
10,000,000, where CPython 2.7 needs almost 3.5 &lt;em&gt;GB&lt;/em&gt;, and CPython 3.x
needs between 1.5 and 2.1 GB &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f6" id="id2"&gt;[6]&lt;/a&gt;. Moving to &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; ("Point3DSlot")
brings the CPython overhead closer to—but still higher than—that of PyPy. In particular, note that the PyPy memory usage is
essentially the same whether or not slots are used.&lt;/p&gt;
&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="chart-caea74a2-ed82-40e5-a929-00ff6d0a0307" class="pygal-chart" viewbox="0 0 800 600"&gt;&lt;!--Generated with pygal 2.4.0 (lxml) ©Kozea 2012-2016 on 2021-12-04--&gt;&lt;!--http://pygal.org--&gt;&lt;!--http://github.com/Kozea/pygal--&gt;&lt;defs&gt;&lt;style type="text/css"&gt;#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307{-webkit-user-select:none;-webkit-font-smoothing:antialiased;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .title{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:16px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .legends .legend text{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:14px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis text{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:10px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis text.major{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:10px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay text.value{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:16px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay text.label{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:10px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:14px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 text.no_data{font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:64px}
#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307{background-color:rgba(249,249,249,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 path,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 rect,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 circle{-webkit-transition:150ms;-moz-transition:150ms;transition:150ms}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .graph &amp;gt; .background{fill:rgba(249,249,249,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .plot &amp;gt; .background{fill:rgba(255,255,255,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .graph{fill:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 text.no_data{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .title{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .legends .legend text{fill:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .legends .legend:hover text{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .line{stroke:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .guide.line{stroke:rgba(0,0,0,.54)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .major.line{stroke:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis text.major{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y .guides:hover .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .line-graph .axis.x .guides:hover .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .stackedline-graph .axis.x .guides:hover .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .xy-graph .axis.x .guides:hover .guide.line{stroke:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .guides:hover text{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .reactive{fill-opacity:.7;stroke-opacity:.8}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .ci{stroke:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .reactive.active,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .active .reactive{fill-opacity:.8;stroke-opacity:.9;stroke-width:4}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .ci .reactive.active{stroke-width:1.5}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .series text{fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip rect{fill:rgba(255,255,255,1);stroke:rgba(0,0,0,1);-webkit-transition:opacity 150ms;-moz-transition:opacity 150ms;transition:opacity 150ms}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .label{fill:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .label{fill:rgba(0,0,0,.87)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .legend{font-size:.8em;fill:rgba(0,0,0,.54)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .x_label{font-size:.6em;fill:rgba(0,0,0,1)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .xlink{font-size:.5em;text-decoration:underline}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip .value{font-size:1.5em}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .bound{font-size:.5em}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .max-value{font-size:.75em;fill:rgba(0,0,0,.54)}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .map-element{fill:rgba(255,255,255,1);stroke:rgba(0,0,0,.54) !important}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .map-element .reactive{fill-opacity:inherit;stroke-opacity:inherit}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-0,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-0 a:visited{stroke:#F44336;fill:#F44336}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-1,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-1 a:visited{stroke:#3F51B5;fill:#3F51B5}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-2,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-2 a:visited{stroke:#009688;fill:#009688}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-3,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-3 a:visited{stroke:#FFC107;fill:#FFC107}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-4,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-4 a:visited{stroke:#FF5722;fill:#FF5722}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-5,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-5 a:visited{stroke:#9C27B0;fill:#9C27B0}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-6,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .color-6 a:visited{stroke:#03A9F4;fill:#03A9F4}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-0 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-1 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-2 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-3 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-4 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-5 text{fill:black}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .text-overlay .color-6 text{fill:black}
#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 text.no_data{text-anchor:middle}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .guide.line{fill:none}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .centered{text-anchor:middle}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .title{text-anchor:middle}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .legends .legend text{fill-opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.x text{text-anchor:middle}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.x:not(.web) text[transform]{text-anchor:start}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.x:not(.web) text[transform].backwards{text-anchor:end}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y text{text-anchor:end}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y text[transform].backwards{text-anchor:start}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y2 text{text-anchor:start}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y2 text[transform].backwards{text-anchor:end}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .guide.line{stroke-dasharray:4,4}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .major.guide.line{stroke-dasharray:6,6}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .horizontal .axis.y .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .horizontal .axis.y2 .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .vertical .axis.x .guide.line{opacity:0}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .horizontal .axis.always_show .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .vertical .axis.always_show .guide.line{opacity:1 !important}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y .guides:hover .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.y2 .guides:hover .guide.line,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis.x .guides:hover .guide.line{opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .axis .guides:hover text{opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .nofill{fill:none}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .subtle-fill{fill-opacity:.2}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .dot{stroke-width:1px;fill-opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .dot.active{stroke-width:5px}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .dot.negative{fill:transparent}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 text,#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 tspan{stroke:none !important}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .series text.active{opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip rect{fill-opacity:.95;stroke-width:.5}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .tooltip text{fill-opacity:1}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .showable{visibility:hidden}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .showable.shown{visibility:visible}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .gauge-background{fill:rgba(229,229,229,1);stroke:none}#chart-caea74a2-ed82-40e5-a929-00ff6d0a0307 .bg-lines{stroke:rgba(249,249,249,1);stroke-width:2px}&lt;/style&gt;&lt;script type="text/javascript"&gt;window.pygal = window.pygal || {};window.pygal.config = window.pygal.config || {};window.pygal.config['caea74a2-ed82-40e5-a929-00ff6d0a0307'] = {"allow_interruptions": false, "box_mode": "extremes", "classes": ["pygal-chart"], "css": ["file://style.css", "file://graph.css"], "defs": [], "disable_xml_declaration": false, "dots_size": 2.5, "dynamic_print_values": false, "explicit_size": false, "fill": false, "force_uri_protocol": "https", "formatter": null, "half_pie": false, "height": 600, "include_x_axis": false, "inner_radius": 0, "interpolate": null, "interpolation_parameters": {}, "interpolation_precision": 250, "inverse_y_axis": false, "js": ["//kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js"], "legend_at_bottom": false, "legend_at_bottom_columns": null, "legend_box_size": 12, "logarithmic": false, "margin": 20, "margin_bottom": null, "margin_left": null, "margin_right": null, "margin_top": null, "max_scale": 16, "min_scale": 4, "missing_value_fill_truncation": "x", "no_data_text": "No data", "no_prefix": false, "order_min": null, "pretty_print": false, "print_labels": false, "print_values": false, "print_values_position": "center", "print_zeroes": true, "range": null, "rounded_bars": null, "secondary_range": null, "show_dots": true, "show_legend": true, "show_minor_x_labels": true, "show_minor_y_labels": true, "show_only_major_dots": false, "show_x_guides": false, "show_x_labels": true, "show_y_guides": true, "show_y_labels": true, "spacing": 10, "stack_from_top": false, "strict": false, "stroke": true, "stroke_style": null, "style": {"background": "rgba(249, 249, 249, 1)", "ci_colors": [], "colors": ["#F44336", "#3F51B5", "#009688", "#FFC107", "#FF5722", "#9C27B0", "#03A9F4", "#8BC34A", "#FF9800", "#E91E63", "#2196F3", "#4CAF50", "#FFEB3B", "#673AB7", "#00BCD4", "#CDDC39", "#9E9E9E", "#607D8B"], "font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "foreground": "rgba(0, 0, 0, .87)", "foreground_strong": "rgba(0, 0, 0, 1)", "foreground_subtle": "rgba(0, 0, 0, .54)", "guide_stroke_dasharray": "4,4", "label_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "label_font_size": 10, "legend_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "legend_font_size": 14, "major_guide_stroke_dasharray": "6,6", "major_label_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "major_label_font_size": 10, "no_data_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "no_data_font_size": 64, "opacity": ".7", "opacity_hover": ".8", "plot_background": "rgba(255, 255, 255, 1)", "stroke_opacity": ".8", "stroke_opacity_hover": ".9", "title_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "title_font_size": 16, "tooltip_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "tooltip_font_size": 14, "transition": "150ms", "value_background": "rgba(229, 229, 229, 1)", "value_colors": [], "value_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "value_font_size": 16, "value_label_font_family": "Consolas, \"Liberation Mono\", Menlo, Courier, monospace", "value_label_font_size": 10}, "title": "Memory Usage (in MB)", "tooltip_border_radius": 0, "tooltip_fancy_mode": true, "truncate_label": null, "truncate_legend": null, "width": 800, "x_label_rotation": 0, "x_labels": ["Point3D", "Point3DSlot", "Point3DSlot Uncached Integers"], "x_labels_major": null, "x_labels_major_count": null, "x_labels_major_every": null, "x_title": null, "xrange": null, "y_label_rotation": 0, "y_labels": null, "y_labels_major": null, "y_labels_major_count": null, "y_labels_major_every": null, "y_title": null, "zero": 0, "legends": ["PyPy2", "PyPy3", "CPython 2.7", "CPython 3.4", "CPython 3.5", "CPython 3.6", "CPython 3.7"]}&lt;/script&gt;&lt;script type="text/javascript" xlink:href="https://kozea.github.io/pygal.js/2.0.x/pygal-tooltips.min.js"&gt;&lt;/script&gt;&lt;/defs&gt;&lt;title&gt;Memory Usage (in MB)&lt;/title&gt;&lt;g class="graph bar-graph vertical"&gt;&lt;rect x="0" y="0" width="800" height="600" class="background"&gt;&lt;/rect&gt;&lt;g transform="translate(168, 46)" class="plot"&gt;&lt;rect x="0" y="0" width="611.6" height="514" class="background"&gt;&lt;/rect&gt;&lt;g class="axis y always_show"&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 504.115385 h611.600000" class="axis major line"&gt;&lt;/path&gt;&lt;text x="-5" y="507.61538461538464" class="major"&gt;0&lt;/text&gt;&lt;title&gt;0&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 446.868025 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="450.36802478788405" class=""&gt;400&lt;/text&gt;&lt;title&gt;400&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 389.620665 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="393.12066496038346" class=""&gt;800&lt;/text&gt;&lt;title&gt;800&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 332.373305 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="335.87330513288293" class=""&gt;1200&lt;/text&gt;&lt;title&gt;1200&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 275.125945 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="278.62594530538234" class=""&gt;1600&lt;/text&gt;&lt;title&gt;1600&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 217.878585 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="221.3785854778818" class=""&gt;2000&lt;/text&gt;&lt;title&gt;2000&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 160.631226 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="164.13122565038123" class=""&gt;2400&lt;/text&gt;&lt;title&gt;2400&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 103.383866 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="106.8838658228807" class=""&gt;2800&lt;/text&gt;&lt;title&gt;2800&lt;/title&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M0.000000 46.136506 h611.600000" class="guide line"&gt;&lt;/path&gt;&lt;text x="-5" y="49.636505995380105" class=""&gt;3200&lt;/text&gt;&lt;title&gt;3200&lt;/title&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="axis x"&gt;&lt;path d="M0.000000 0.000000 v514.000000" class="line"&gt;&lt;/path&gt;&lt;g class="guides"&gt;&lt;path d="M109.774359 0.000000 v514.000000" class="guide line"&gt;&lt;/path&gt;&lt;text x="109.77435897435896" y="529.0" class=""&gt;Point3D&lt;/text&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M305.800000 0.000000 v514.000000" class="guide line"&gt;&lt;/path&gt;&lt;text x="305.8" y="529.0" class=""&gt;Point3DSlot&lt;/text&gt;&lt;/g&gt;&lt;g class="guides"&gt;&lt;path d="M501.825641 0.000000 v514.000000" class="guide line"&gt;&lt;/path&gt;&lt;text x="501.82564102564106" y="529.0" class=""&gt;Point3DSlot Uncached Integers&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-0 color-0"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="25.00167032967033" y="405.0917639537655" rx="0" ry="0" width="21.68603663003663" height="99.02362066161913" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;691.9&lt;/desc&gt;&lt;desc class="x centered"&gt;35.844688644688645&lt;/desc&gt;&lt;desc class="y centered"&gt;454.6035742845751&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="221.02731135531135" y="405.07745211380865" rx="0" ry="0" width="21.68603663003663" height="99.037932501576" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;692&lt;/desc&gt;&lt;desc class="x centered"&gt;231.87032967032968&lt;/desc&gt;&lt;desc class="y centered"&gt;454.5964183645966&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="417.0529523809524" y="339.2143646322692" rx="0" ry="0" width="21.68603663003663" height="164.90101998311542" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1152.2&lt;/desc&gt;&lt;desc class="x centered"&gt;427.8959706959707&lt;/desc&gt;&lt;desc class="y centered"&gt;421.6648746238269&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-1 color-1"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="49.64489377289377" y="406.27964667018614" rx="0" ry="0" width="21.68603663003663" height="97.8357379451985" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;683.6&lt;/desc&gt;&lt;desc class="x centered"&gt;60.48791208791208&lt;/desc&gt;&lt;desc class="y centered"&gt;455.1975156427854&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="245.6705347985348" y="406.2510229902724" rx="0" ry="0" width="21.68603663003663" height="97.86436162511222" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;683.8&lt;/desc&gt;&lt;desc class="x centered"&gt;256.5135531135531&lt;/desc&gt;&lt;desc class="y centered"&gt;455.18320380282853&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="441.69617582417584" y="340.44947642054757" rx="0" ry="0" width="21.68603663003663" height="163.66590819483707" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1143.57&lt;/desc&gt;&lt;desc class="x centered"&gt;452.53919413919414&lt;/desc&gt;&lt;desc class="y centered"&gt;422.2824305179661&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-2 color-2"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="74.2881172161172" y="9.884615384615358" rx="0" ry="0" width="21.68603663003663" height="494.2307692307693" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;3453.3&lt;/desc&gt;&lt;desc class="x centered"&gt;85.13113553113551&lt;/desc&gt;&lt;desc class="y centered"&gt;257.0&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="270.31375824175825" y="391.349535227174" rx="0" ry="0" width="21.68603663003663" height="112.76584938821065" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;787.92&lt;/desc&gt;&lt;desc class="x centered"&gt;281.15677655677655&lt;/desc&gt;&lt;desc class="y centered"&gt;447.7324599212793&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="466.33939926739924" y="271.5909208360342" rx="0" ry="0" width="21.68603663003663" height="232.52446377935047" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1624.7&lt;/desc&gt;&lt;desc class="x centered"&gt;477.18241758241754&lt;/desc&gt;&lt;desc class="y centered"&gt;387.8531527257094&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-3 color-3"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="98.93134065934066" y="286.37505151148616" rx="0" ry="0" width="21.68603663003663" height="217.74033310389848" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1521.4&lt;/desc&gt;&lt;desc class="x centered"&gt;109.77435897435898&lt;/desc&gt;&lt;desc class="y centered"&gt;395.2452180634354&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="294.95698168498166" y="402.6158156412261" rx="0" ry="0" width="21.68603663003663" height="101.49956897415854" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;709.2&lt;/desc&gt;&lt;desc class="x centered"&gt;305.79999999999995&lt;/desc&gt;&lt;desc class="y centered"&gt;453.36560012830535&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="490.9826227106227" y="271.6138197799652" rx="0" ry="0" width="21.68603663003663" height="232.50156483541946" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1624.54&lt;/desc&gt;&lt;desc class="x centered"&gt;501.825641025641&lt;/desc&gt;&lt;desc class="y centered"&gt;387.8646021976749&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-4 color-4"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="123.5745641025641" y="282.84002704213805" rx="0" ry="0" width="21.68603663003663" height="221.2753575732466" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1546.1&lt;/desc&gt;&lt;desc class="x centered"&gt;134.41758241758242&lt;/desc&gt;&lt;desc class="y centered"&gt;393.4777058287614&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="319.6002051282051" y="402.6444393211399" rx="0" ry="0" width="21.68603663003663" height="101.47094529424476" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;709&lt;/desc&gt;&lt;desc class="x centered"&gt;330.4432234432234&lt;/desc&gt;&lt;desc class="y centered"&gt;453.37991196826226&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="515.6258461538462" y="271.5909208360342" rx="0" ry="0" width="21.68603663003663" height="232.52446377935047" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1624.7&lt;/desc&gt;&lt;desc class="x centered"&gt;526.4688644688645&lt;/desc&gt;&lt;desc class="y centered"&gt;387.8531527257094&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-5 color-5"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="148.21778754578753" y="286.24624495187436" rx="0" ry="0" width="21.68603663003663" height="217.86913966351028" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1522.3&lt;/desc&gt;&lt;desc class="x centered"&gt;159.06080586080586&lt;/desc&gt;&lt;desc class="y centered"&gt;395.1808147836295&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="344.2434285714286" y="403.31709579911296" rx="0" ry="0" width="21.68603663003663" height="100.79828881627168" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;704.3&lt;/desc&gt;&lt;desc class="x centered"&gt;355.0864468864469&lt;/desc&gt;&lt;desc class="y centered"&gt;453.7162402072488&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="540.2690695970696" y="271.5766089960773" rx="0" ry="0" width="21.68603663003663" height="232.53877561930733" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1624.8&lt;/desc&gt;&lt;desc class="x centered"&gt;551.1120879120879&lt;/desc&gt;&lt;desc class="y centered"&gt;387.845996805731&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="series serie-6 color-6"&gt;&lt;g class="bars"&gt;&lt;g class="bar"&gt;&lt;rect x="172.861010989011" y="195.7095453846822" rx="0" ry="0" width="21.68603663003663" height="308.40583923070244" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;2154.9&lt;/desc&gt;&lt;desc class="x centered"&gt;183.70402930402932&lt;/desc&gt;&lt;desc class="y centered"&gt;349.9124650000334&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3D&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="368.886652014652" y="359.6230484107732" rx="0" ry="0" width="21.68603663003663" height="144.49233620461143" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;1009.6&lt;/desc&gt;&lt;desc class="x centered"&gt;379.7296703296703&lt;/desc&gt;&lt;desc class="y centered"&gt;431.8692165130789&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot&lt;/desc&gt;&lt;/g&gt;&lt;g class="bar"&gt;&lt;rect x="564.9122930402931" y="93.42282521289559" rx="0" ry="0" width="21.68603663003663" height="410.69255940248905" class="rect reactive tooltip-trigger"&gt;&lt;/rect&gt;&lt;desc class="value"&gt;2869.6&lt;/desc&gt;&lt;desc class="x centered"&gt;575.7553113553114&lt;/desc&gt;&lt;desc class="y centered"&gt;298.76910491414014&lt;/desc&gt;&lt;desc class="x_label"&gt;Point3DSlot Uncached Integers&lt;/desc&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="titles"&gt;&lt;text x="400.0" y="26" class="title plot_title"&gt;Memory Usage (in MB)&lt;/text&gt;&lt;/g&gt;&lt;g transform="translate(168, 46)" class="plot overlay"&gt;&lt;g class="series serie-0 color-0"&gt;&lt;/g&gt;&lt;g class="series serie-1 color-1"&gt;&lt;/g&gt;&lt;g class="series serie-2 color-2"&gt;&lt;/g&gt;&lt;g class="series serie-3 color-3"&gt;&lt;/g&gt;&lt;g class="series serie-4 color-4"&gt;&lt;/g&gt;&lt;g class="series serie-5 color-5"&gt;&lt;/g&gt;&lt;g class="series serie-6 color-6"&gt;&lt;/g&gt;&lt;/g&gt;&lt;g transform="translate(168, 46)" class="plot text-overlay"&gt;&lt;g class="series serie-0 color-0"&gt;&lt;/g&gt;&lt;g class="series serie-1 color-1"&gt;&lt;/g&gt;&lt;g class="series serie-2 color-2"&gt;&lt;/g&gt;&lt;g class="series serie-3 color-3"&gt;&lt;/g&gt;&lt;g class="series serie-4 color-4"&gt;&lt;/g&gt;&lt;g class="series serie-5 color-5"&gt;&lt;/g&gt;&lt;g class="series serie-6 color-6"&gt;&lt;/g&gt;&lt;/g&gt;&lt;g transform="translate(168, 46)" class="plot tooltip-overlay"&gt;&lt;g transform="translate(0 0)" style="opacity: 0" class="tooltip"&gt;&lt;rect rx="0" ry="0" width="0" height="0" class="tooltip-box"&gt;&lt;/rect&gt;&lt;g class="text"&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g transform="translate(10, 56)" class="legends"&gt;&lt;g id="activate-serie-0" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="1.0" width="12" height="12" class="color-0 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="11.2"&gt;PyPy2&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-1" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="22.0" width="12" height="12" class="color-1 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="32.2"&gt;PyPy3&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-2" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="43.0" width="12" height="12" class="color-2 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="53.2"&gt;CPython 2.7&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-3" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="64.0" width="12" height="12" class="color-3 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="74.2"&gt;CPython 3.4&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-4" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="85.0" width="12" height="12" class="color-4 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="95.2"&gt;CPython 3.5&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-5" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="106.0" width="12" height="12" class="color-5 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="116.2"&gt;CPython 3.6&lt;/text&gt;&lt;/g&gt;&lt;g id="activate-serie-6" class="legend reactive activate-serie"&gt;&lt;rect x="0.0" y="127.0" width="12" height="12" class="color-6 reactive"&gt;&lt;/rect&gt;&lt;text x="17.0" y="137.2"&gt;CPython 3.7&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g transform="translate(790, 56)" class="legends"&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;p&gt;The third group of data is the same as the second group, except
instead of using small integers that should be in the CPython internal
integer object cache &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f7" id="id3"&gt;[7]&lt;/a&gt;, I used larger numbers that shouldn't be
cached. This is just an interesting data point showing the allocation
of three times as many objects, and won't be discussed further.&lt;/p&gt;
&lt;!-- A link to 'Do Re Mi' from the Sound of Music: "Let's start at the
very beginning, a very good place to start." --&gt;
&lt;div class="section" id="what-is-being-measured"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id9"&gt;What Is Being Measured&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the script I used to produce these numbers &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f2" id="id4"&gt;[2]&lt;/a&gt;, I'm using the
excellent psutil library's &lt;a class="reference external" href="http://psutil.readthedocs.io/en/latest/#psutil.Process.memory_full_info"&gt;Process.memory_info&lt;/a&gt; to record the
"unique set size" ("the memory which is unique to a process and which
would be freed if the process was terminated right now") before and
then after allocating a large number of objects.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_987899094c92461da96de736c74b6b89-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_memory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uss&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-3"&gt;&lt;/a&gt;    &lt;span class="n"&gt;inst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-4"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Size of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizeof_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;inst&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Count      AbsoluteUsage     Delta"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-8"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"======================================="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-11"&gt;&lt;/a&gt;        &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-12"&gt;&lt;/a&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-14"&gt;&lt;/a&gt;            &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;klass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-15"&gt;&lt;/a&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-16"&gt;&lt;/a&gt;        &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_memory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uss&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-17"&gt;&lt;/a&gt;        &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;%9d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;global_starting_memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uss&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-18"&gt;&lt;/a&gt;              &lt;span class="n"&gt;format_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-19"&gt;&lt;/a&gt;        &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;a name="rest_code_987899094c92461da96de736c74b6b89-20"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This gives us a fairly accurate idea of how much memory the processes
needed to allocate from the operating system to be able to create all
the objects we asked for. (&lt;tt class="docutils literal"&gt;get_memory&lt;/tt&gt; is a helper function that
runs the garbage collector to be sure we have the most stable
numbers.)&lt;/p&gt;
&lt;div class="section" id="what-is-not-being-measured"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id10"&gt;What Is Not Being Measured&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this example output from a run of PyPy, the &lt;tt class="docutils literal"&gt;AbsoluteUsage&lt;/tt&gt; is
the total growth from when the program started, while the &lt;tt class="docutils literal"&gt;Delta&lt;/tt&gt; is
the growth just within this function.&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-1"&gt;&lt;/a&gt;Memory for Point3D(1, 2, 3)
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-2"&gt;&lt;/a&gt;Count      AbsoluteUsage     Delta
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-3"&gt;&lt;/a&gt;=======================================
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-4"&gt;&lt;/a&gt;      100           0.02           0.02
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-5"&gt;&lt;/a&gt;     1000           0.03           0.03
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-6"&gt;&lt;/a&gt;    10000           0.51           0.51
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-7"&gt;&lt;/a&gt;   100000           7.20           7.20
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-8"&gt;&lt;/a&gt;  1000000          69.11          69.11
&lt;a name="rest_code_abea1e7bbd544248b67bbed92ef5690b-9"&gt;&lt;/a&gt; 10000000         691.90         691.90
&lt;/pre&gt;&lt;p&gt;This was the first of the test runs within this particular process.
The second test run within this process reports higher absolute deltas
since the beginning of the program, although the overall deltas are
smaller. This indicates how much memory the program has allocated from
the operating system but not returned to it, even though it may
technically free from the standpoint of the Python runtime; this
accounts for things like internal caches, or in PyPy's case, jitted
code.&lt;/p&gt;
&lt;pre class="code text"&gt;&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-1"&gt;&lt;/a&gt;Memory for Point3DSlot(1, 2, 3)
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-2"&gt;&lt;/a&gt;Size of Point3DSlot -1
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-3"&gt;&lt;/a&gt;Count      AbsoluteUsage     Delta
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-4"&gt;&lt;/a&gt;=======================================
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-5"&gt;&lt;/a&gt;      100          86.09           0.00
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-6"&gt;&lt;/a&gt;     1000          86.12           0.03
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-7"&gt;&lt;/a&gt;    10000          86.56           0.46
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-8"&gt;&lt;/a&gt;   100000          87.33           1.23
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-9"&gt;&lt;/a&gt;  1000000         138.70          52.60
&lt;a name="rest_code_7993a3a16f054fb88b67afc16912f188-10"&gt;&lt;/a&gt; 10000000         692.05         605.95
&lt;/pre&gt;&lt;p&gt;Although I captured the data, this post is not about the startup or
initial memory allocation of the various interpreters, nor about how
much can easily be shared between forked processes, nor about how much
memory is returned to the operating system while the process is still
running. We're only talking about the memory size needed to allocate a
given number of objects, e.g., the &lt;tt class="docutils literal"&gt;Delta&lt;/tt&gt; column.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="object-internals"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id11"&gt;Object Internals&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To understand what's happening, let's look at the two types of objects
we're comparing:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point3D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-4"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-5"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-6"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-8"&gt;&lt;/a&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point3DSlot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-10"&gt;&lt;/a&gt;    &lt;span class="vm"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'y'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'z'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-12"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-13"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-14"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;a name="rest_code_8e9442e5c8044e6e9d61b819a8739b43-15"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;These are both small classes with three instance attributes. One is a
standard, default, object, and one specifies its instance attributes
in &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="section" id="objects-with-dictionaries"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id12"&gt;Objects with Dictionaries&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Standard objects, like &lt;tt class="docutils literal"&gt;Point3D&lt;/tt&gt;, have &lt;a class="reference external" href="https://docs.python.org/3/library/stdtypes.html#object.__dict__"&gt;a special attribute&lt;/a&gt;
&lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;, that is a normal Python dictionary object that is used
to hold all the instance attributes for the object. We &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes-ignore-getattribute"&gt;previously
looked at&lt;/a&gt;
how &lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt; can be used to customize &lt;em&gt;all&lt;/em&gt; attribute
reads for an object; likewise, &lt;tt class="docutils literal"&gt;__setattr__&lt;/tt&gt; can &lt;a class="reference external" href="https://docs.python.org/3/reference/datamodel.html?highlight=__slots__#object.__setattr__"&gt;customize all
attribute writes&lt;/a&gt;. The default &lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;__setattr__&lt;/tt&gt; that a class inherits from &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; function
something like they were written to access the &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-5"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-8"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__setattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_35364c4900ba43f6989e0f32507241fe-9"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;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;value&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;One advantage of having a &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; underlying an object is the
flexibility it provides: you don't have to pre-declare your attributes
for every object, and any object can have any attribute, so it
facilitates subclasses adding new attributes, or even other libraries
adding new, specialized, attributes to &lt;a class="reference external" href="https://github.com/zopefoundation/zope.cachedescriptors/blob/master/src/zope/cachedescriptors/property.py#L60"&gt;implement caching of expensive
computed properties&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One disadvantage is that a &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; is a generic Python
dictionary, not specialized at all &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f3" id="id5"&gt;[3]&lt;/a&gt;, and as such it has overhead.&lt;/p&gt;
&lt;p&gt;On CPython, we can ask the interpreter how much memory any given
object uses with &lt;a class="reference external" href="https://docs.python.org/3/library/sys.html?highlight=getsizeof#sys.getsizeof"&gt;sys.getsizeof&lt;/a&gt;. On my machine under a 64-bit
CPython 2.7.15, a bare &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; takes 16 bytes, while a trivial
subclass takes a full 64 bytes (due to the overhead of being tracked
by the garbage collector):&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_15c85954908d4deea792def9308294e8-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;a name="rest_code_15c85954908d4deea792def9308294e8-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_15c85954908d4deea792def9308294e8-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;16&lt;/span&gt;
&lt;a name="rest_code_15c85954908d4deea792def9308294e8-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrivialSubclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_15c85954908d4deea792def9308294e8-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TriviasSubclass&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_15c85954908d4deea792def9308294e8-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;64&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;An empty &lt;tt class="docutils literal"&gt;dict&lt;/tt&gt; occupies 280 bytes:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_760e244c17cc4461bd35fcf1c2a071a4-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;a name="rest_code_760e244c17cc4461bd35fcf1c2a071a4-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;280&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;And so when you combine the size of the trivial subclass, with the
size of its &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; you arrive at a minimum object size of 344
bytes:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_6bd67988897f4e7aa9efb4ae67d5cd9b-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrivialSubclass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6bd67988897f4e7aa9efb4ae67d5cd9b-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;280&lt;/span&gt;
&lt;a name="rest_code_6bd67988897f4e7aa9efb4ae67d5cd9b-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrivialSubclass&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TrivialSubclass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6bd67988897f4e7aa9efb4ae67d5cd9b-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;344&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;A fully occupied &lt;tt class="docutils literal"&gt;Point3D&lt;/tt&gt; object is also 344 bytes:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_5aa26ad4ef524067a5faaf256fbf198a-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point3D&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_5aa26ad4ef524067a5faaf256fbf198a-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_5aa26ad4ef524067a5faaf256fbf198a-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;344&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Because of the way dictionaries are implemented &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f8" id="id6"&gt;[8]&lt;/a&gt;, there's always a
little spare room for extra attributes. We don't find a jump in size
until we've added three more attributes:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;344&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;344&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_84331c6c162b4d0aa0fc2988f2269d64-9"&gt;&lt;/a&gt;&lt;span class="go"&gt;1112&lt;/span&gt;
&lt;/pre&gt;&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;These values can change quite a bit across Python versions,
typically improving over time. In CPython 3.4 and 3.5,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;getsizeof({})&lt;/span&gt;&lt;/tt&gt; returns 288, while it returns 240 in both 3.6 and
3.7. In addition, &lt;tt class="docutils literal"&gt;getsizeof(pd.__dict__)&lt;/tt&gt; returns 96 and 112
&lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f4" id="id7"&gt;[4]&lt;/a&gt;. The answer to &lt;tt class="docutils literal"&gt;getsizeof(pd)&lt;/tt&gt; is 56 in all four versions.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="objects-with-slots"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id13"&gt;Objects With Slots&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Objects with a &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; declaration, like &lt;tt class="docutils literal"&gt;Point3DSlot&lt;/tt&gt; do not
have a &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; by default. &lt;a class="reference external" href="https://docs.python.org/3/reference/datamodel.html?highlight=__slots__#slots"&gt;The documentation&lt;/a&gt; notes that this
can be a space savings. Indeed, on CPython 2.7, a &lt;tt class="docutils literal"&gt;Point3DSlot&lt;/tt&gt; has
a size of only 72 bytes, only one full pointer larger than a trivial
subclass (when we do not factor in the &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;):&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_d017747e3a124be2b2826720fa3695b0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Point3DSlot&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_d017747e3a124be2b2826720fa3695b0-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_d017747e3a124be2b2826720fa3695b0-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;72&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If they don't have an instance dictionary, where do they store their
attributes? And why, if &lt;tt class="docutils literal"&gt;Point3DSlot&lt;/tt&gt; has &lt;em&gt;three&lt;/em&gt; defined
attributes, is it only &lt;em&gt;one&lt;/em&gt; pointer larger than &lt;tt class="docutils literal"&gt;Point3D&lt;/tt&gt;?&lt;/p&gt;
&lt;p&gt;Slots, like &lt;tt class="docutils literal"&gt;@property&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;@classmethod&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;@staticmethod&lt;/tt&gt;, are
implemented using &lt;a class="reference external" href="https://docs.python.org/3/reference/datamodel.html?highlight=__slots__#implementing-descriptors"&gt;descriptors&lt;/a&gt;. For our purpose, descriptors are a
way to extend the workings of &lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt; and friends. A
&lt;em&gt;descriptor&lt;/em&gt; is an object whose type implements a &lt;tt class="docutils literal"&gt;__get__&lt;/tt&gt; method,
and when that object is found in a type's dictionary, it is called
instead of checking the &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;. Something like this &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#f5" id="id8"&gt;[5]&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-2"&gt;&lt;/a&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-5"&gt;&lt;/a&gt;           &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'__get__'&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-6"&gt;&lt;/a&gt;           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__get__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-9"&gt;&lt;/a&gt;             &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-10"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-11"&gt;&lt;/a&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-12"&gt;&lt;/a&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__setattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-14"&gt;&lt;/a&gt;           &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'__set__'&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-15"&gt;&lt;/a&gt;           &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__set__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-16"&gt;&lt;/a&gt;           &lt;span class="k"&gt;return&lt;/span&gt;
&lt;a name="rest_code_d45069f1cc0542c98355758867c7b60b-17"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;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;value&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;When the &lt;tt class="docutils literal"&gt;class&lt;/tt&gt; statement (indeed, when the &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; metaclass)
finds &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; in the class body (the class dictionary), &lt;a class="reference external" href="https://github.com/python/cpython/blob/2bea7716093012319b5e6a4260fe802b15031f21/Objects/typeobject.c#L2198"&gt;it
takes special steps&lt;/a&gt;. Most importantly, it creates a descriptor for
each mentioned slot and places it in the class's &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;. So our
&lt;tt class="docutils literal"&gt;Point3DSlot&lt;/tt&gt; class gets three such descriptors:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_52404834930f448f93ac28ded4530957-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point3DSlot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__dict__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;{'__doc__': None,&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;'__init__': &amp;lt;function __main__.__init__&amp;gt;,&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;'__module__': '__main__',&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;'__slots__': ('x', 'y', 'z'),&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;'x': &amp;lt;member 'x' of 'Point3DSlot' objects&amp;gt;,&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;'y': &amp;lt;member 'y' of 'Point3DSlot' objects&amp;gt;,&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;'z': &amp;lt;member 'z' of 'Point3DSlot' objects&amp;gt;}&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;pds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;1&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Point3DSlot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__get__&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;wrapper&lt;/span&gt; &lt;span class="s1"&gt;'__get__'&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;member_descriptor&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x10b6fc2d8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-13"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Point3DSlot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__get__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Point3DSlot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_52404834930f448f93ac28ded4530957-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;div class="section" id="variable-storage"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id14"&gt;Variable Storage&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We've established &lt;em&gt;how&lt;/em&gt; we can access these magic, hidden slotted
attributes (through the descriptor protocol). (We've also established
why we can't have defaults for slotted attributes in the class.) But
we still haven't found out &lt;em&gt;where&lt;/em&gt; they are stored. If they're not in
a dictionary, where are they?&lt;/p&gt;
&lt;p&gt;The answer is that they're stored directly in the object itself. Every
type has a member called &lt;a class="reference external" href="https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_basicsize"&gt;tp_basicsize&lt;/a&gt;, exposed to Python as
&lt;tt class="docutils literal"&gt;__basicsize__&lt;/tt&gt;. When the interpreter allocates an object, it
allocates &lt;tt class="docutils literal"&gt;__basicsize__&lt;/tt&gt; bytes for it (every object has a minimum
basic size, the size of &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;). The &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; metaclass arranges
for &lt;tt class="docutils literal"&gt;__basicsize__&lt;/tt&gt; to be big enough to hold (a pointer to) each of
the slotted attributes, which are kept in memory immediately after the
data for the basic &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; . The descriptor for each attribute,
then, just does some pointer arithmetic off of &lt;tt class="docutils literal"&gt;self&lt;/tt&gt; to read and
write the value. In a way, it's very similar to how
&lt;a class="reference external" href="https://docs.python.org/3/library/collections.html#collections.namedtuple"&gt;collections.namedtuple&lt;/a&gt; works, except using pointers instead of
indices.&lt;/p&gt;
&lt;p&gt;That may be hard to follow, so here's an example.&lt;/p&gt;
&lt;p&gt;The basic size of &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; exactly matches the reported size of its
instances:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_3b89ee9664cb44d39aef7aaf29824d45-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__basicsize__&lt;/span&gt;
&lt;a name="rest_code_3b89ee9664cb44d39aef7aaf29824d45-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;16&lt;/span&gt;
&lt;a name="rest_code_3b89ee9664cb44d39aef7aaf29824d45-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_3b89ee9664cb44d39aef7aaf29824d45-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;16&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We get the same when we create an object that cannot have any instance
variables, and hence does not need to be tracked by the garbage
collector:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NoSlots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="vm"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;NoSlots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__basicsize__&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;16&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NoSlots&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_bc429f3cd44a4f40b1378d5a1e376db3-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;16&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;When we add one slot to an object, its basic size increases by one
pointer (8 bytes), and because that object could be tracked by the
garbage collector, &lt;em&gt;this&lt;/em&gt; object needs to be tracked by the collector,
so &lt;tt class="docutils literal"&gt;getsizeof&lt;/tt&gt; reports some extra overhead:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OneSlot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="vm"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;OneSlot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__basicsize__&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;24&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OneSlot&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_8303f43b487e49eb8fb83627a0c89ab7-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;56&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The basic size for an object with 3 slots is 16 (the size of &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;) + 3 pointers, or 40.
What's the basic size for an object that has a &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;?&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_8811138c4f784191b1eb4e84635d7a7f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Point3DSlot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__basicsize__&lt;/span&gt;
&lt;a name="rest_code_8811138c4f784191b1eb4e84635d7a7f-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;40&lt;/span&gt;
&lt;a name="rest_code_8811138c4f784191b1eb4e84635d7a7f-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__basicsize__&lt;/span&gt;
&lt;a name="rest_code_8811138c4f784191b1eb4e84635d7a7f-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;32&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Hmm, it's 16 + &lt;em&gt;2&lt;/em&gt; pointers. What could those two pointers be?
Documentation to the rescue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; allow us to explicitly declare data members (like
properties) and deny the creation of &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;__weakref__&lt;/tt&gt;  (unless explicitly declared in &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;...)&lt;/blockquote&gt;
&lt;p&gt;So those two pointers are for &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;__weakref__&lt;/tt&gt;, things
that standard objects get automatically, but which we have to opt-in
to if we want them with &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;. Thus, an object with three
slots is one pointer size bigger than a standard object.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="how-pypy-does-better"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id15"&gt;How PyPy Does Better&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By now we should understand why the memory usage dropped significantly
when we added &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; to our objects on CPython (although that
comes with a cost). That leaves the question: how does PyPy get such
good memory performance with a &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; that &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; doesn't
even matter?&lt;/p&gt;
&lt;p&gt;Earlier I wrote that the &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; of an instance is just a
standard dictionary, not specialized at all. That's basically true on
CPython, but it's not at all true on PyPy. PyPy basically fakes
&lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; by using &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; for all objects.&lt;/p&gt;
&lt;p&gt;A given set of attributes (such as our "x", "y", "z" attributes for
&lt;tt class="docutils literal"&gt;Point3DSlot&lt;/tt&gt;) is called a "map". Each instance refers to its map,
which tells PyPy how to efficiently access a given attribute. When an
attribute is added or deleted, a new map is created (or re-used from
an existing object; objects of completely unrelated types, but having
common attributes can share the same maps) and assigned to the object,
re-arranging things as needed. It's as if &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt; was assigned
to each instance, with descriptors added and removed for the instance
on the fly.&lt;/p&gt;
&lt;p&gt;If the program ever directly accesses an instance's &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;, PyPy
creates a thin wrapper object that operates on the object's map.&lt;/p&gt;
&lt;p&gt;So for a program that has many simalar looking objects, even if
unrelated, PyPy's approach can save a lot of memory. On the other
hand, if the program creates objects that have a very diverse set of
attributes, and that program frequently directly accessess
&lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt;, it's theoretically possible that PyPy could use &lt;em&gt;more&lt;/em&gt;
memory than CPython.&lt;/p&gt;
&lt;p&gt;You can read more about this approach in &lt;a class="reference external" href="https://morepypy.blogspot.com/2010/11/efficiently-implementing-python-objects.html#using-maps-for-memory-efficient-instances"&gt;this PyPy blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;All 64-bit builds, all tested on macOS. The results on Linux
were very similar.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id4"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Available at &lt;a class="reference external" href="https://gist.github.com/jamadden/8c1aa567013e2eed546c0a1dff584263"&gt;this gist&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id5"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In CPython. But I'm getting ahead of myself.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id7"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The CPython dict implementation was &lt;a class="reference external" href="https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-compactdict"&gt;completely overhauled in
CPython 3.6&lt;/a&gt;.
And based on the sizes of &lt;tt class="docutils literal"&gt;{}&lt;/tt&gt; versus &lt;tt class="docutils literal"&gt;pd.__dict__&lt;/tt&gt; we
can see some sort of specialization for instance
dictionaries, at least in terms of their fill factor.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id8"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This is very rough, and actually inaccurate in some small but
important details. Refer to the documentation for the full protocol.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id2"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;No, I'm not totally sure why Python 3.7 is such an outlier
and uses more memory than the other Python 3.x versions.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f7" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id3"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;See &lt;a class="reference external" href="https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong"&gt;PyLong_FromLong&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f8" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html#id6"&gt;[8]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;With a particular desired &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Hash_table#Key_statistics"&gt;load factor&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>cpython</category><category>memory</category><category>pypy</category><category>python</category><guid>https://seecoresoftware.com/blog/2018/08/cpython-vs-pypy-memory-usage.html</guid><pubDate>Wed, 08 Aug 2018 13:22:07 GMT</pubDate></item><item><title>Class Warfare in Python 2</title><link>https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;In Python 2, there are two distinct types of classes, the so-called
"old" (or "classic") and (&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Mostly_Harmless"&gt;increasingly inaccurately named&lt;/a&gt;) "new" style
classes. This post will discuss the differences between the two as
visible to the Python programmer, and explore a little bit of the
implementation decisions behind those differences.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#new-style-classes" id="id4"&gt;New Style Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes" id="id5"&gt;Old Style Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#practically-speaking" id="id6"&gt;Practically Speaking&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#operators" id="id7"&gt;Operators&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-instances-are-bigger" id="id8"&gt;Old Style Instances are Bigger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes-ignore-slots" id="id9"&gt;Old Style Classes Ignore &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes-ignore-getattribute" id="id10"&gt;Old Style Classes Ignore &lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes-have-a-simplistic-mro" id="id11"&gt;Old Style Classes Have a Simplistic MRO&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-style-classes-are-slow" id="id12"&gt;Old Style Classes are Slow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#implementing-old-style-classes" id="id13"&gt;Implementing Old Style Classes&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#cpython-types" id="id14"&gt;CPython Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#instance-types" id="id15"&gt;instance types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#implementing-old-style-classes-in-python" id="id16"&gt;Implementing Old Style Classes in Python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="new-style-classes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id4"&gt;New Style Classes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;New style classes were introduced in &lt;a class="reference external" href="https://docs.python.org/2.7/whatsnew/2.2.html#peps-252-and-253-type-and-class-changes"&gt;Python 2.2&lt;/a&gt;
back in December of 2001. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#f1" id="id1"&gt;[1]&lt;/a&gt; New style classes are most familiar
to modern Python programmers and are the only kind of classes
available in Python 3.&lt;/p&gt;
&lt;p&gt;Let's look at some of the observable properties of new style classes.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_d205b669d53c4fd8ae158bd5bfbd00c9-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_d205b669d53c4fd8ae158bd5bfbd00c9-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;In Python 2, we create new style classes by (explicitly or implicitly)
inheriting from &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#f3" id="id2"&gt;[2]&lt;/a&gt;. This means that the class
&lt;tt class="docutils literal"&gt;NewStyle&lt;/tt&gt; is an instance of &lt;cite&gt;type&lt;/cite&gt;, that is, &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; is the
metaclass (alternately, we can say that any instance of &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; is a
new style class):&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;
&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;class 'NewStyle'&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_0f94009ca89f48c488c0310547b4d729-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'type'&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Instances of this class are, well, instances of this class, and their
type is this class:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_e203502f9aff488c980c5ad0bf2de6a9-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_e203502f9aff488c980c5ad0bf2de6a9-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;NewStyle&lt;/span&gt;
&lt;a name="rest_code_e203502f9aff488c980c5ad0bf2de6a9-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_e203502f9aff488c980c5ad0bf2de6a9-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_e203502f9aff488c980c5ad0bf2de6a9-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Unsurprisingly, instances of NewStyle classes are also instances of
&lt;tt class="docutils literal"&gt;object&lt;/tt&gt;; it's right there in the class definition and the class
&lt;tt class="docutils literal"&gt;__bases__&lt;/tt&gt; and the &lt;a class="reference external" href="https://docs.python.org/2/library/stdtypes.html?highlight=mro#class.mro"&gt;method resolution order&lt;/a&gt; (MRO):&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__bases__&lt;/span&gt;
&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;(&amp;lt;type 'object'&amp;gt;,)&lt;/span&gt;
&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mro&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_520c327d97dd4b41be6087907fb438de-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;[&amp;lt;class 'NewStyle'&amp;gt;, &amp;lt;type 'object'&amp;gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The default &lt;a class="reference external" href="https://docs.python.org/2/library/functions.html#repr"&gt;repr&lt;/a&gt; of a new style instance includes its type's name:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_5476f491147e4878aada53cfa666bad1-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_5476f491147e4878aada53cfa666bad1-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;NewStyle object at 0x...&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="old-style-classes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id5"&gt;Old Style Classes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If that's a new style class, what's an old style class? Old style, or
"classic" classes, were the only type of class available in Python 2.1
and earlier. For backwards compatibility when moving older code to
Python 2.2 or later, they remained the default (we'll see shortly some
of the practical behaviour differences between old and new style classes).&lt;/p&gt;
&lt;p&gt;This means that we create an old style class by not specifying any
ancestors. We also create old style classes when only specifying
ancestors that themselves are old style classes.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_bcd80a3e85ea4d14b4427e688dbec74c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_bcd80a3e85ea4d14b4427e688dbec74c-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Let's look at the same observable properties as we did with new style
classes and compare them. We've already seen that we didn't specify
&lt;tt class="docutils literal"&gt;object&lt;/tt&gt; in the inheritance list. Is this class a &lt;tt class="docutils literal"&gt;type&lt;/tt&gt;?&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;
&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;class __builtin__.OldStyle at 0x...&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;False&lt;/span&gt;
&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_74b96cbc0e034c9199902e052d83e369-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'classobj'&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If you've been paying attention, you're not surprised to see that old
style classes are &lt;em&gt;not&lt;/em&gt; instances of &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; (after all, that's the
definition we used for new style classes). They have a different repr,
which is not too surprising, but they also have a different type.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_3be7c9d5bd5041148178d2460695094e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_3be7c9d5bd5041148178d2460695094e-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'type'&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Hmm, and the type of that different type (i.e., its metaclass) &lt;em&gt;is&lt;/em&gt;
type. That makes me wonder, do all old-style classes share a type?
Let's continue exploring.&lt;/p&gt;
&lt;p&gt;Instances of the old style class are of course still instances of the
old style class, but their type is &lt;em&gt;not&lt;/em&gt; that class, unlike with new
style classes:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;OldStyle&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;False&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_872b173d77534430983b915200ad3aa0-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Instead, their type is something called &lt;a class="reference external" href="https://docs.python.org/2/library/types.html#types.InstanceType"&gt;instance&lt;/a&gt;. Now I'm wondering
again, do all &lt;em&gt;instances&lt;/em&gt; of old style classes share a type? If so,
how does class-specific behaviour get implemented?&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldStyle2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;old_style2_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldStyle2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style2_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style2_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_ab2df80eb4f94dd3a8e583c6388136b1-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;They do! Curiouser and curiouser. How is type-specific behaviour
implemented then? Let's keep going.&lt;/p&gt;
&lt;p&gt;We saw that new style classes are instances of &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;, and it
exists in their &lt;tt class="docutils literal"&gt;__bases__&lt;/tt&gt; and MRO. What about old classes?&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__bases__&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;()&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mro&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-6"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-7"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_69b823a509254d54a11fa36d5200175e-8"&gt;&lt;/a&gt;&lt;span class="gr"&gt;AttributeError&lt;/span&gt;: &lt;span class="n"&gt;class OldStyle has no attribute 'mro'&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Strangely, the old style instance &lt;em&gt;is&lt;/em&gt; still an instance of
&lt;tt class="docutils literal"&gt;object&lt;/tt&gt;, even though it doesn't inherit from it, as seen by it not
being in the &lt;tt class="docutils literal"&gt;__bases__&lt;/tt&gt; (and it doesn't even have the concept of an
exposed MRO).&lt;/p&gt;
&lt;p&gt;Finally, the repr of old style classes is also different:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_931c8e05237f427a89f630a746d85118-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_931c8e05237f427a89f630a746d85118-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;__builtin__.OldStyle instance at 0x...&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="practically-speaking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id6"&gt;Practically Speaking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OK, that's enough time spent down in the weeds dealing with subtle
differences in inheritance bases and reprs. Before we talk about
implementation, are there any &lt;em&gt;practical&lt;/em&gt; differences a Python
programmer should be concerned about, or at least know about?&lt;/p&gt;
&lt;p&gt;Yes. I'll hit some of the highlights here, there are probably others.
(TL;DR: Always write new-style classes.)&lt;/p&gt;
&lt;div class="section" id="operators"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id7"&gt;Operators&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For new style classes, all the Python special "dunder" methods (like
&lt;tt class="docutils literal"&gt;__repr__&lt;/tt&gt; and the arithmetic and comparison operators such as
&lt;tt class="docutils literal"&gt;__add__&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt;) must be &lt;a class="reference external" href="https://docs.python.org/2/reference/datamodel.html#new-style-special-lookup"&gt;defined on the class itself&lt;/a&gt;. If we
want to change the repr, we can't do it in the instance, we have to do
so on the class:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hi, I'm a new instance"&lt;/span&gt;
&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;NewStyle object at 0x...&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;NewStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hi, I'm the new class"&lt;/span&gt;
&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7135d1be1a9d4996b9599bf1daa858ec-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;Hi, I'm the new class&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;(We monkey-patched a class after its definition here, which does work
thanks to some magic well discuss soon, but that's not usually the way
its done.)&lt;/p&gt;
&lt;p&gt;In contrast, old style classes allowed dunders to be assigned to an
instance, even overriding one set on the class, in exactly the same
way that regular methods work on both new and old style classes.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hi, I'm the old class"&lt;/span&gt;
&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Hi, I'm the old class&lt;/span&gt;
&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Hi, I'm the old instance"&lt;/span&gt;
&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_7f1f156732f44845b5d369030ba4576f-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;Hi, I'm the old instance&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="old-style-instances-are-bigger"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id8"&gt;Old Style Instances are Bigger&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On a 64-bit platform, old style instances are one pointer size larger:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_a08b50d88a57440c989256e52e21caf5-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;a name="rest_code_a08b50d88a57440c989256e52e21caf5-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_a08b50d88a57440c989256e52e21caf5-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;64&lt;/span&gt;
&lt;a name="rest_code_a08b50d88a57440c989256e52e21caf5-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_style_instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_a08b50d88a57440c989256e52e21caf5-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;72&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="old-style-classes-ignore-slots"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id9"&gt;Old Style Classes Ignore &lt;tt class="docutils literal"&gt;__slots__&lt;/tt&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.python.org/2/reference/datamodel.html#slots"&gt;slots&lt;/a&gt; declaration can be used not only to optimize memory
usage for small frequently used objects but offer tighter control on
what attributes an instance can have, which may be useful in some
scenarios. Unfortunately, old style classes accept but ignore this
declaration.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewWithSlots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;     &lt;span class="vm"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'attr'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;new_with_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewWithSlots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_with_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;56&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;new_with_slots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;not_a_slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-7"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-8"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-9"&gt;&lt;/a&gt;&lt;span class="gr"&gt;AttributeError&lt;/span&gt;: &lt;span class="n"&gt;'NewWithSlots' object has no attribute 'not_a_slot'&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;new_with_slots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_with_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;56&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-13"&gt;&lt;/a&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-14"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldWithSlots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-15"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;     &lt;span class="vm"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'attr'&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-16"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;old_with_slots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldWithSlots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-17"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_with_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-18"&gt;&lt;/a&gt;&lt;span class="go"&gt;72&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-19"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;old_with_slots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;not_a_slot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-20"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_with_slots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_cc6979aceb384399bd60ab6d86144376-21"&gt;&lt;/a&gt;&lt;span class="go"&gt;72&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="section" id="old-style-classes-ignore-getattribute"&gt;
&lt;span id="old-getattribute"&gt;&lt;/span&gt;&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id10"&gt;Old Style Classes Ignore &lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt;&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://docs.python.org/2/reference/datamodel.htmlmore-attribute-access-for-new-style-classes"&gt;getattribute&lt;/a&gt; dunder method allows an instance to intercept
&lt;em&gt;all&lt;/em&gt; attribute access. It's what makes it possible to implement
&lt;a class="reference external" href="https://github.com/zopefoundation/persistent/blob/master/persistent/persistence.py"&gt;transparently persistent objects in pure-Python&lt;/a&gt;. Indeed, it's the
same basic feature that allowed for &lt;a class="reference external" href="https://docs.python.org/2/reference/datamodel.html#implementing-descriptors"&gt;descriptors&lt;/a&gt; to be implemented
in Python 2.2. Old style classes only support &lt;tt class="docutils literal"&gt;__getattr__&lt;/tt&gt;, which
is only used when the attribute cannot be found.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewWithCustomAttrs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Looking up &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&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;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'computed'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'computed'&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Returning default for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&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;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewWithCustomAttrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;Looking up foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;Returning default for foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;1&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-14"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-15"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;Looking up foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-17"&gt;&lt;/a&gt;&lt;span class="go"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-18"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-19"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_new&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computed&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-20"&gt;&lt;/a&gt;&lt;span class="go"&gt;Looking up computed&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-21"&gt;&lt;/a&gt;&lt;span class="go"&gt;'computed'&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-22"&gt;&lt;/a&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-23"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldWithCustomAttrs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-24"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-25"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Looking up &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&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;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-26"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'computed'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'computed'&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-27"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__getattribute__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-28"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getattr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-29"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Returning default for &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;"&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;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-30"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-31"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldWithCustomAttrs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-32"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-33"&gt;&lt;/a&gt;&lt;span class="go"&gt;Returning default for foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-34"&gt;&lt;/a&gt;&lt;span class="go"&gt;1&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-35"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-36"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-37"&gt;&lt;/a&gt;&lt;span class="go"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-38"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-39"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;custom_old&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computed&lt;/span&gt;
&lt;a name="rest_code_9b4e6bcf5ff04576bd39d20227cc5a79-40"&gt;&lt;/a&gt;&lt;span class="go"&gt;42&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Contrary to some information that can be found on the Internet, old
style classes &lt;em&gt;can&lt;/em&gt; use most descriptors, including &lt;cite&gt;@property&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="old-style-classes-have-a-simplistic-mro"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id11"&gt;Old Style Classes Have a Simplistic MRO&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When multiple inheritance is involved, the MRO of an old style class
can be quite surprising. I'll let &lt;a class="reference external" href="http://python-history.blogspot.com/2010/06/method-resolution-order.html"&gt;Guido provide the explanation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="old-style-classes-are-slow"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id12"&gt;Old Style Classes are Slow&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is especially &lt;a class="reference external" href="https://github.com/zopefoundation/ZODB/pull/160"&gt;true on PyPy&lt;/a&gt;, but even on CPython 2.7 an old style
class is three times slower at basic arithmetic.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__add__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new_add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewAdd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Cheating, sneak this into the globals&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-6"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'new_add + 1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;0.2...&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-8"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldAdd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__add__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-10"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;old_add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OldAdd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'old_add + 1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_1959631a0f27411391bf55cc09e7136b-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;0.6...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="implementing-old-style-classes"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id13"&gt;Implementing Old Style Classes&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The shift to new style classes represents something of a shift away
from Python's earlier free-wheeling days when it could almost be
thought of as a prototype-based language into something more
traditionally class based. Yet Python 2.2 up through Python 2.7 still
support the old way of doing things. It would be silly to imagine
there are two completely separate object and type hierarchy
implementations inside the CPython interpreter, along with two
separate ways of doing arithmetic and getting reprs, etc &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#f4" id="id3"&gt;[3]&lt;/a&gt;, yet the old
style classes continue to work as always. How is this done?&lt;/p&gt;
&lt;p&gt;We've had some tantalizing clues to help point us in the right
direction.&lt;/p&gt;
&lt;!-- the manuel test directive to ignore the next block cannot be indented --&gt;
&lt;!-- ignore-next-block --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Regarding instances of old style classes, even separate classes are
the same type. The same is not true for new style classes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Old style instances are still instances of &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Regarding old style classes themselves, they are &lt;em&gt;not&lt;/em&gt; instances of
&lt;tt class="docutils literal"&gt;type&lt;/tt&gt;, rather of &lt;tt class="docutils literal"&gt;classobj&lt;/tt&gt;...which itself is an instance of
&lt;tt class="docutils literal"&gt;type&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Regarding perfomance, the change to method lookup "provides
significant scope for speed optimisations within the interpreter".&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;If you followed the link to see what was new in Python 2.2, you'd
see that one of the other major features introduced by &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0253/"&gt;PEP 253&lt;/a&gt;
was the ability to subclass built-in types like &lt;tt class="docutils literal"&gt;list&lt;/tt&gt; and
override their operators. In Python
2.1, just as with old style classes today, all classes implemented
in Python shared the same type, but classes implemented in C (like
&lt;tt class="docutils literal"&gt;list&lt;/tt&gt;) could be distinct types; importantly, types were not
classes. Here's Python 2.1:&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldStyle2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldStyle&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-7"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OldStyle2&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-9"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;type 'list'&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-13"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-14"&gt;&lt;/a&gt;&lt;span class="c"&gt;...&lt;/span&gt;
&lt;a name="rest_code_690bc23d671a414e9e98806ccbd6b815-15"&gt;&lt;/a&gt;&lt;span class="gr"&gt;TypeError&lt;/span&gt;: &lt;span class="n"&gt;base is not a class object&lt;/span&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To further understand how CPython implements both old and new-style
classes, you need to understand just a bit about how it represents
types internally and how it invokes those special dunder methods.&lt;/p&gt;
&lt;div class="section" id="cpython-types"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id14"&gt;CPython Types&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In CPython, all types are defined by a C structure called
&lt;a class="reference external" href="https://docs.python.org/2.7/c-api/typeobj.html"&gt;PyTypeObject&lt;/a&gt;. It has a series of members (confusingly also called
slots) that are C function pointers that implement the behaviour
defined by the type. For example, &lt;a class="reference external" href="https://docs.python.org/2.7/c-api/typeobj.html#c.PyTypeObject.tp_repr"&gt;tp_repr&lt;/a&gt; is the slot for the
&lt;tt class="docutils literal"&gt;__repr__&lt;/tt&gt; function, and &lt;a class="reference external" href="https://docs.python.org/2.7/c-api/typeobj.html#c.PyTypeObject.tp_getattro"&gt;tp_getattro&lt;/a&gt; is the slot for the
&lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt; function. When the interpreter needs to get the
repr of an object, or get an attribute from it, it &lt;em&gt;always&lt;/em&gt; goes
through those function pointers. An operation like &lt;tt class="docutils literal"&gt;repr(obj)&lt;/tt&gt;
becomes the C equivalent of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;type(obj).__repr__(obj)&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;When we create a new style class, the standard &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; metaclass
makes a C function pointer out of each special dunder method defined
in the class's &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; and uses that to fill in the slots in the
C structure. Special methods that aren't present are inherited.
Similarly, when we later &lt;em&gt;set&lt;/em&gt; such an attribute, the &lt;tt class="docutils literal"&gt;__setattr__&lt;/tt&gt;
&lt;a class="reference external" href="https://github.com/python/cpython/blob/2bea7716093012319b5e6a4260fe802b15031f21/Objects/typeobject.c#L2727"&gt;function of the type metaclass&lt;/a&gt; checks to see if it needs to update
the C slots.&lt;/p&gt;
&lt;p&gt;The C structure that defines the &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; type uses
&lt;a class="reference external" href="https://docs.python.org/2.7/c-api/object.html#c.PyObject_GenericGetAttr"&gt;PyObject_GenericGetAttr&lt;/a&gt; for its &lt;tt class="docutils literal"&gt;tp_getattro&lt;/tt&gt;
(&lt;tt class="docutils literal"&gt;__getattribute__&lt;/tt&gt;) slot. That function implements the "standard"
way of accessing attributes of objects. It's the same thing we showed
above in &lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#old-getattribute"&gt;old_getattribute&lt;/a&gt; when we called
&lt;tt class="docutils literal"&gt;object.__getattribute__(self, name)&lt;/tt&gt;. The &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; type also
leaves most other slots (like for &lt;tt class="docutils literal"&gt;__add__&lt;/tt&gt;) empty, meaning that
subclasses must define them in the class &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; (or assign to
them) for them to be found.&lt;/p&gt;
&lt;p&gt;Importantly, though, other types implemented in C don't have to do
&lt;em&gt;any&lt;/em&gt; of that. They could set their &lt;tt class="docutils literal"&gt;tp_getattro&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;tp_repr&lt;/tt&gt;
slots, along with any of the other slots, to do whatever they want.&lt;/p&gt;
&lt;p&gt;You might now understand how PEP 253, which gave us new style classes,
also gave us the ability to subclass C types.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="instance-types"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id15"&gt;instance types&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;That's exactly what &lt;tt class="docutils literal"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/tt&gt; does. That type is defined
by &lt;a class="reference external" href="https://github.com/python/cpython/blob/2bea7716093012319b5e6a4260fe802b15031f21/Objects/classobject.c#L2202"&gt;PyInstance_Type&lt;/a&gt;. The  metaclass &lt;tt class="docutils literal"&gt;classobj&lt;/tt&gt; (recall that's the
type of old style classes) sets its &lt;tt class="docutils literal"&gt;tp_call&lt;/tt&gt; slot (aka
&lt;tt class="docutils literal"&gt;__call__&lt;/tt&gt;) to &lt;a class="reference external" href="https://github.com/python/cpython/blob/2bea7716093012319b5e6a4260fe802b15031f21/Objects/classobject.c#L549"&gt;PyInstance_New&lt;/a&gt;, a function that returns a new
object of &lt;tt class="docutils literal"&gt;&amp;lt;type 'instance'&amp;gt;&lt;/tt&gt;, aka &lt;tt class="docutils literal"&gt;PyInstance_Type&lt;/tt&gt; (explaining
while all old instances have the same type).
(Incidentally, the metaclass also returns instances of itself, not new
sub-types as the &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; metaclass does, which explains why they all
have the same type.)&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;PyInstance_Type&lt;/tt&gt;, in turn, overrides almost &lt;em&gt;all&lt;/em&gt; of the possible
slot definitions to implement the old behaviour of first looking in
the instance's &lt;tt class="docutils literal"&gt;__dict__&lt;/tt&gt; and only if it's not present then looking
up the class hierarchy or invoking &lt;tt class="docutils literal"&gt;__getattr__&lt;/tt&gt;. For example,
&lt;tt class="docutils literal"&gt;tp_repr&lt;/tt&gt; is &lt;a class="reference external" href="https://github.com/python/cpython/blob/2bea7716093012319b5e6a4260fe802b15031f21/Objects/classobject.c#L897"&gt;instance_repr&lt;/a&gt;, which uses &lt;tt class="docutils literal"&gt;instance_getattr&lt;/tt&gt; to
dynamically find an appropriate &lt;tt class="docutils literal"&gt;__repr__&lt;/tt&gt; function from the
instance or class. This, by the way, is the source of the performance
difference between old and new style classes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="implementing-old-style-classes-in-python"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id16"&gt;Implementing Old Style Classes in Python&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Can we re-implement old style classes in pure-Python (for example, on
Python 3)? I haven't tried, but we can probably get pretty close. It
would be similar to implementing transparent &lt;a class="reference external" href="https://github.com/zopefoundation/zope.proxy/blob/master/src/zope/proxy/__init__.py#L87"&gt;object proxies in
pure-Python&lt;/a&gt;, which is verbose and tedious.&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Thus demonstrating &lt;a class="reference external" href="https://www.tvfanatic.com/quotes/as-for-you-you-will-be-submerged-in-royal-slurm-which-in-a-mat/"&gt;the perils&lt;/a&gt; of naming something "new."
Here we are 17 years later and still calling them that.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p class="first"&gt;Or by explicitly specifying a &lt;tt class="docutils literal"&gt;__metaclass__&lt;/tt&gt; of &lt;tt class="docutils literal"&gt;type&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="last"&gt;&lt;pre class="code pycon"&gt;&lt;a name="rest_code_318500158d094f69b10158c59c21ddb5-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewWithMeta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_318500158d094f69b10158c59c21ddb5-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="vm"&gt;__metaclass__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;
&lt;a name="rest_code_318500158d094f69b10158c59c21ddb5-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewWithMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_318500158d094f69b10158c59c21ddb5-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html#id3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Good programmers are lazy.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>history</category><category>implementation</category><category>python</category><category>python2</category><guid>https://seecoresoftware.com/blog/2018/07/python-2-new-vs-old-classes.html</guid><pubDate>Sat, 28 Jul 2018 14:17:07 GMT</pubDate></item><item><title>The Xerox Alto: gevent for hardware</title><link>https://seecoresoftware.com/blog/2018/07/alto-gevent-for-hardware.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gnu-pth-gevent-for-c.html"&gt;Previously&lt;/a&gt; I discussed the
&lt;a class="reference external" href="https://www.gnu.org/software/pth/"&gt;GNU Pth&lt;/a&gt; library which implements cooperative multi-tasking for the
C programming language. That's pretty low level. But I recently read
about cooperative multi-tasking at an even lower level: inside the
microcode of the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Xerox_Alto"&gt;Xerox Alto&lt;/a&gt; central processor! It's hard to get
much lower level than that.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;p&gt;The engineers at Seattle's &lt;a class="reference external" href="http://livingcomputers.org"&gt;Living Computers museum&lt;/a&gt; maintain &lt;a class="reference external" href="https://engblg.livingcomputers.org"&gt;a
blog&lt;/a&gt; where they write about their experiences repairing and
maintaining the historic computers that make up the museum's exhibits
and fill its labs.&lt;/p&gt;
&lt;p&gt;In a two-part series (&lt;a class="reference external" href="https://engblg.livingcomputers.org/index.php/2017/04/21/the-xerox-alto-an-introduction/"&gt;1&lt;/a&gt;, &lt;a class="reference external" href="https://engblg.livingcomputers.org/index.php/2017/06/23/the-xerox-alto-part-2-microcode/"&gt;2&lt;/a&gt;), one of the engineers discusses the
Xerox Alto, the first computer designed specifically to run a
graphical user interface. Graphical user interfaces were very new at
the time, so there was a lot of experimentation to do. In order to
make that experimentation quick and easy, the very CPU of the Alto was
itself programmable in microcode.&lt;/p&gt;
&lt;p&gt;The second article discusses the microcode in detail. Microcoding CPUs
may have been uncommon in 1973 when the Alto was created, but it's
common enough today that most computer science programs will include
an introduction to it, so there was nothing too surprising. Until I
got to the bottom in the section called "Tasks":&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To allow the devices in the Alto to share the Alto's processor with
minimal overhead, the Alto's designers developed very simple
cooperative task-switching hardware.&lt;/p&gt;
&lt;p&gt;Conceptually, the processor is shared between sixteen microcode
Tasks, with a priority assigned to each one: Task 0 is the lowest
priority task, and Task 15 the highest. Each device in the Alto
has one or more tasks dedicated to it. To make task switching fast
and cheap to implement in hardware, the only state saved by the
task-switching hardware for each Task is that task's program
counter — the MPC ("Micro Program Counter"").&lt;/p&gt;
&lt;p&gt;Only one task is running on the processor at a time, and at any
time the microcode for a task may invoke a "task switch" function
(named, strangely enough, TASK). When a TASK instruction is
executed, the processor selects the highest priority task that
needs to run (is "Requesting Wakeup") and switches to it by
jumping to the instruction pointed to by that Task’s MPC.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wow—greenlets implemented in hardware! I've &lt;a class="reference external" href="http://www.cpushack.com"&gt;read about a lot
of CPUs&lt;/a&gt;, and this is the first time I remember reading about one
with cooperative task-switching at the microcode level.&lt;/p&gt;
&lt;p&gt;It's a stretch to call this gevent for hardware, of course. (For one
thing, there doesn't seem to be any concept of an event loop.) But
programmers still had to worry about &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html"&gt;yielding&lt;/a&gt; to other "greenlets" in a
timely fashion:&lt;/p&gt;
&lt;blockquote&gt;
Since the Alto’s Task system is cooperative (meaning that task
switches only happen when microcode explicitly requests them) the
microcode for each task must be careful to yield to other tasks every
so often so that they don’t get starved of time. If the Disk Word Task
is late in execution, data from the disk is corrupted. If the Display
Word Task doesn’t get enough time, the display will flicker or display
glitches.&lt;/blockquote&gt;&lt;/div&gt;</description><category>gevent</category><category>hardware</category><category>history</category><category>python</category><guid>https://seecoresoftware.com/blog/2018/07/alto-gevent-for-hardware.html</guid><pubDate>Thu, 05 Jul 2018 16:17:07 GMT</pubDate></item><item><title>Blocking gevent's Hub Part 2: Finding Blocking</title><link>https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html"&gt;Last time&lt;/a&gt; we looked at the
causes and consequences of greenlets that block and avoid switching to
the hub. Many times such greenlets (really, the code they run) are
obvious, but some times they are not.&lt;/p&gt;
&lt;p&gt;This post will look at some new features that gevent 1.3 added to make
finding greenlets that block easier. These can be used both during
development and in production.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#what-it-does" id="id5"&gt;What It does&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#testing-and-asserting" id="id6"&gt;Testing And Asserting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#how-it-works" id="id7"&gt;How It Works&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#performance" id="id8"&gt;Performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="what-it-does"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id5"&gt;What It does&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gevent 1.3 &lt;a class="reference external" href="http://www.gevent.org/monitoring.html#blocking"&gt;includes a couple features&lt;/a&gt; to help find code that's
blocking. One of those we can turn on using just environment
variables, no code changes needed (although you can configure it in
code as well). This makes it especially handy for debugging on the
fly.&lt;/p&gt;
&lt;p&gt;Let's take a look at one of our blocking examples from last time:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-7"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&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;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-13"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.1f&lt;/span&gt;&lt;span class="s2"&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;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now we'll tell gevent to let us know if it detects any greenlets
blocking the hub and run the example (the duration of time that a
greenlet is allowed to block before it gets reported is configurable;
we'll go with the defaults here):&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;GEVENT_MONITOR_THREAD_ENABLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; python /tmp/test.py
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;================================================================================&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-06-22T19:20:10Z : Greenlet &amp;lt;Greenlet "Greenlet-0" at 0x1.: f&amp;gt; appears to be blocked&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Reported by &amp;lt;gevent.__tracer.GreenletTracer object at 0x...&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;Blocked Stack (for thread id 0x...):&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;  File "/tmp/test.py", line 5, in f&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;    time.sleep(3)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;Info:&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;* Threads&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;Thread 0x2 (MainThread)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-15"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;  File "/tmp/test.py", line 5, in f&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-17"&gt;&lt;/a&gt;&lt;span class="go"&gt;    time.sleep(3)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-18"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-19"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-20"&gt;&lt;/a&gt;&lt;span class="go"&gt;* Greenlets&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-21"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-22"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;greenlet.greenlet object at 0x3&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-23"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Parent: None&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-24"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Monitoring Thread:&amp;lt;PeriodicMonitoringThread &amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-25"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;greenlet.greenlet object at 0x4&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-26"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Parent: None&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-27"&gt;&lt;/a&gt;&lt;span class="go"&gt; +--- &amp;lt;Greenlet "Greenlet-0" at 0x1: f&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-28"&gt;&lt;/a&gt;&lt;span class="go"&gt; :          Parent: &amp;lt;Hub '' at 0x5 select default pending=0 ref=1 thread_ident=0x2&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-29"&gt;&lt;/a&gt;&lt;span class="go"&gt; :          Spawned at:&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-30"&gt;&lt;/a&gt;&lt;span class="go"&gt; :            File "/tmp/test.py", line 8, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-31"&gt;&lt;/a&gt;&lt;span class="go"&gt; :              greenlet = gevent.spawn(f)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-32"&gt;&lt;/a&gt;&lt;span class="go"&gt; +--- &amp;lt;Hub '' at 0x5 select default pending=0 ref=1 thread_ident=0x2&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-33"&gt;&lt;/a&gt;&lt;span class="go"&gt;            Parent: &amp;lt;greenlet.greenlet object at 0x4&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-34"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-35"&gt;&lt;/a&gt;&lt;span class="go"&gt; .&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-36"&gt;&lt;/a&gt;&lt;span class="go"&gt; Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This time, gevent detected the hold up and printed a report about
what the problem was. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id3" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-and-asserting"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id6"&gt;Testing And Asserting&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Either in unittests, from particularly latency sensitive parts of your
application, or when trying to further narrow down the cause of
blocking, gevent provides the &lt;a class="reference external" href="http://www.gevent.org/api/gevent.util.html#gevent.util.assert_switches"&gt;assert_switches()&lt;/a&gt; context manager.
Wrap this manager around some code to be sure that it switches to
another greenlet or back to the hub, optionally within a set amount of
time. If nothing switches, then an exception will be raised with the
same sort of information as above.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gevent.util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# This will always raise an exception: nothing switches&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-6"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-7"&gt;&lt;/a&gt;  File &lt;span class="nb"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;, line &lt;span class="m"&gt;2&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-8"&gt;&lt;/a&gt;  File &lt;span class="nb"&gt;"//gevent/util.py"&lt;/span&gt;, line &lt;span class="m"&gt;592&lt;/span&gt;, in &lt;span class="n"&gt;__exit__&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;_FailedToSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report_lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-10"&gt;&lt;/a&gt;&lt;span class="gr"&gt;gevent.util._FailedToSwitch&lt;/span&gt;: &lt;span class="n"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# This will never raise an exception; nothing switched,&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="c1"&gt;# but it happened very fast&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-13"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_blocking_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-14"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-15"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="how-it-works"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id7"&gt;How It Works&lt;/a&gt;&lt;/h2&gt;
&lt;div class="admonition admonition-credit"&gt;
&lt;p class="first admonition-title"&gt;Credit&lt;/p&gt;
&lt;p class="last"&gt;This technique did not originate with gevent. I've seen it in &lt;a class="reference external" href="http://www.rfk.id.au/blog/entry/detect-gevent-blocking-with-greenlet-settrace/"&gt;this
blog post from 2012 by Ryan Kelly&lt;/a&gt;
and I've seen it in &lt;a class="reference external" href="https://github.com/mozilla-services/mozservices/blob/master/mozsvc/gunicorn_worker.py"&gt;this code&lt;/a&gt;
from Mozilla. gevent 1.3 just generalized it, made it safe for
multiple threads and hubs, and incorporated it into the core of
gevent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The main question we want to be able to answer is: did the hub get to
run during a certain time period? Running the hub means switching into
its greenlet, so what we really want to know is: was the greenlet hub
switched into?&lt;/p&gt;
&lt;p&gt;The greenlet library provides a hook well suited for this purpose:
&lt;a class="reference external" href="https://greenlet.readthedocs.io/en/latest/#tracing-support"&gt;greenlet.settrace()&lt;/a&gt;. This functions similarly to &lt;a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace"&gt;sys.settrace()&lt;/a&gt;
that's part of the core interpreter. The later calls a function for
every "interesting" interpreter event such as a function call, and the
former calls a function for every interesting event involving
greenlets.&lt;/p&gt;
&lt;p&gt;Since greenlets can really only do two interesting things,
&lt;tt class="docutils literal"&gt;'switch'&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;'throw'&lt;/tt&gt; are the only two defined events—for
our purposes they are equivalent as they both involve a transfer of
control from one greenlet to another, and both events tell us which
greenlet is going to be taking control (the "target").&lt;/p&gt;
&lt;p&gt;So to answer our question, we only need to keep track of whether or
not the greenlet hub was the target of an event and thus assumes
control. For the decorator &lt;tt class="docutils literal"&gt;assert_switches&lt;/tt&gt;, which has a
well-defined scope and only needs to answer that question at the exact
time it is exited, that's enough &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id4" id="id2"&gt;[2]&lt;/a&gt;, but for the general monitoring
function that lets you know if (when) it &lt;em&gt;ever&lt;/em&gt; found the hub blocked
for longer than a certain period, we need to do a little more. To be
able to answer that question meaningfully, we also need to know the
timestamp of the last event entering the hub so we can compare it to
the current timestamp: if it's been "too long," then we can raise an
alert.&lt;/p&gt;
&lt;div class="section" id="performance"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id8"&gt;Performance&lt;/a&gt;&lt;/h3&gt;
&lt;div class="sidebar"&gt;
&lt;p class="first sidebar-title"&gt;Implementation Details Alert&lt;/p&gt;
&lt;p class="last"&gt;This post discusses implementation details of gevent 1.3. These
details generally should not be relied upon other than as
&lt;a class="reference external" href="http://gevent.org"&gt;documented&lt;/a&gt;, as they may change in the future.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;gevent is a performance critical library, so monitoring features that
are too perfomance sucking to enable are a non-starter. For that
reason, the implementation of these features was carefully optimized
and a few different tradeoffs are made. Here are some examples.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Getting the current time (&lt;tt class="docutils literal"&gt;time.time()&lt;/tt&gt;) is relatively expensive,
so we try to avoid doing that too often. For example, instead of
checking if we've exceeded the blocking threshold on every greenlet
switch, we only do so periodically in a background thread.&lt;/li&gt;
&lt;li&gt;We use some heuristics to detect if we switched into the hub instead
of absolute timestamps.&lt;/li&gt;
&lt;li&gt;The greenlet trace functions are compiled with Cython and strongly
typed. This makes the relatively complex work that the blocking
monitor trace function needs to do &lt;a class="reference external" href="https://github.com/gevent/gevent/pull/1190"&gt;faster than the most trivial
trace function implemented in Python&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As always, if you find bugs or can think of enhancements or additions
to gevent, please feel free to &lt;a class="reference external" href="https://github.com/gevent/gevent/issues"&gt;open an issue&lt;/a&gt; or &lt;a class="reference external" href="https://github.com/gevent/gevent/pulls"&gt;create a pull request&lt;/a&gt;!&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="id3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I admit I cheated here in the output a little bit. I trimmed
it: it detected, and reported, the same blockage several
times. It should probably recognize that and just print
something about "same blockage". I should fix that. Also, I fixed up the spawning
line. Things spawned at the module level do not always get the
right traceback.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="id4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;As long as you don't use the &lt;tt class="docutils literal"&gt;max_blocking_time&lt;/tt&gt; parameter.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>1.3</category><category>blocking</category><category>debugging</category><category>gevent</category><category>greenlet</category><category>hub</category><category>implementation</category><category>python</category><category>series</category><category>tracing</category><guid>https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html</guid><pubDate>Wed, 27 Jun 2018 14:36:00 GMT</pubDate></item><item><title>Blocking gevent's Hub Part 1: Understanding Blocking</title><link>https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html"&gt;In the beginning&lt;/a&gt; we talked about gevent's
hub and how greenlets switch in and out of it to implement IO.
&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/05/implementing-gevent-locks.html"&gt;Following that&lt;/a&gt; we showed
how locks in gevent are implemented much the same way, by "parking" a
waiting greenlet, switching to the hub to let other greenlets run or
do IO, and eventually switching back to the parked greenlet.&lt;/p&gt;
&lt;p&gt;That's a lot of switching. What does it mean if that switching doesn't
happen? What should a programmer know about switching and its
opposite, blocking? (There's also &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html"&gt;part 2&lt;/a&gt;.)&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#cooperation" id="id5"&gt;Cooperation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#examples" id="id6"&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#synergism-matters" id="id7"&gt;Synergism Matters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#avoiding-blocking" id="id8"&gt;Avoiding Blocking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#diagnosing-blocking" id="id9"&gt;Diagnosing Blocking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="cooperation"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id5"&gt;Cooperation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gevent (and greenlets in general) is a &lt;a class="reference external" href="http://www.gevent.org/intro.html#cooperative-multitasking"&gt;cooperative multitasking&lt;/a&gt;
system, meaning that any given greenlet runs until it chooses to give
up control. Under gevent, choosing to give up control is usually
automatic, and usually happens when the greenlet wants to handle IO,
such as reading or writing a socket to handle an incoming HTTP request
or HTTP response. A greenlet can also give up control automatically by
waiting for a lock that another greenlet owns, or it can explicitly
give up control by using the &lt;a class="reference external" href="http://www.gevent.org/api/gevent.html#gevent.sleep"&gt;gevent.sleep()&lt;/a&gt; API. In all those
cases, control passes to the hub (recall that the hub is the greenlet
running the event loop); this is referred to as &lt;em&gt;yielding&lt;/em&gt; to the hub.
When a greenlet yields to the hub, another greenlet gets a chance to
run or IO gets serviced by the event loop. A greenlet that yields to
the hub is called &lt;em&gt;cooperative&lt;/em&gt; (or sometimes &lt;em&gt;green&lt;/em&gt;). When a
greenlet doesn't yield, it's &lt;em&gt;non-cooperative&lt;/em&gt;.&lt;/p&gt;
&lt;div class="section" id="examples"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id6"&gt;Examples&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Of course, it's not actually the greenlet itself that's yielding, it's
the code that the greenlet is running that yields. We can thus say
that any given piece of code is &lt;em&gt;cooperative&lt;/em&gt; or &lt;em&gt;green&lt;/em&gt; or not. Here
are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;gevent.sleep()&lt;/tt&gt; is cooperative&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-7"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&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;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-13"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.0f&lt;/span&gt;&lt;span class="s2"&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;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;On my system, that prints three dots, indicating that the greenlets
are yielding to the hub and switching back and forth:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;...&lt;/span&gt;
&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I will omit the timing part of the main greenlet in the rest of the
examples unless it changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;time.sleep()&lt;/tt&gt; is &lt;strong&gt;not&lt;/strong&gt; cooperative&lt;/p&gt;
&lt;p&gt;Here's that same example, with the inner greenlet using
&lt;a class="reference external" href="https://docs.python.org/3/library/time.html#time.sleep"&gt;time.sleep()&lt;/a&gt; from the standard library.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;pre class="code console"&gt;&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We get only one dot, indicating that as soon as the main greenlet
yielded to the hub, which in turn handed control over to the
greenlet running &lt;tt class="docutils literal"&gt;f&lt;/tt&gt;, that greenlet failed to yield again until it
was completely finished.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id="cpu-bound"&gt;
&lt;li&gt;&lt;p class="first"&gt;Code that is CPU-bound is &lt;strong&gt;not&lt;/strong&gt; cooperative&lt;/p&gt;
&lt;p&gt;If a function is in a tight computation loop, there is no opportunity for it
to yield control.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-6"&gt;&lt;/a&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-7"&gt;&lt;/a&gt;        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;pre class="code console"&gt;&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 28.2&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If that had been cooperative, we would have expected to see about 28
dots, since that's how long the inner greenlet ran. We only got one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Socket IO using &lt;a class="reference external" href="http://www.gevent.org/api/gevent.socket.html#module-gevent.socket"&gt;gevent.socket&lt;/a&gt; is cooperative&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent.socket&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.python.org'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-9"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-11"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: We switch to a smaller sleep duration&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-13"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-14"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.1f&lt;/span&gt;&lt;span class="s2"&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;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Lots of dots here.&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.....................&lt;/span&gt;
&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 0.0&lt;/span&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Socket IO using the standard &lt;a class="reference external" href="https://docs.python.org/3/library/socket.html#module-socket"&gt;socket&lt;/a&gt; is &lt;strong&gt;not&lt;/strong&gt; cooperative &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id3" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.python.org'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Again, only one dot, meaning no yielding.&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 0.0&lt;/span&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;C extension libraries &lt;strong&gt;may&lt;/strong&gt; or &lt;strong&gt;may not&lt;/strong&gt; be cooperative&lt;/p&gt;
&lt;p&gt;This is a tricky area. C extensions are so common in Python (much of
the standard library is implemented in C) that you may not even
realize you're using one. Commonly, C extensions are used for two
major purposes.&lt;/p&gt;
&lt;p&gt;The first is to accelerate CPU intensive code (for example,
&lt;a class="reference external" href="http://www.numpy.org"&gt;numpy&lt;/a&gt; to speed up linear algebra). These are probably no more or
less cooperative than a counterpart written in Python. (I'll explain
what I mean by that in a minute.)&lt;/p&gt;
&lt;p&gt;The second is to integrate with other existing native (C) libraries,
such as &lt;a class="reference external" href="https://pillow.readthedocs.io/en/5.1.x/"&gt;imaging libraries&lt;/a&gt; or &lt;a class="reference external" href="https://pypi.org/project/mysqlclient/"&gt;database clients&lt;/a&gt;. Some of these
are essentially CPU accelerators, so they basically fall into the
first category. But libraries that want to do IO, especially talking
to remote systems, particularly databases, are likely to want to
handle IO their own way and, without special hooks, are unlikely to
be cooperative. (The PostgreSQL driver and its Python C extension
&lt;a class="reference external" href="https://pypi.org/project/psycopg2/"&gt;psycopg2&lt;/a&gt; is an example of a database driver that provides the
necessary hooks to be made cooperative using &lt;a class="reference external" href="https://pypi.org/project/psycogreen/"&gt;psycogreen&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Back to that "no more or less cooperative than a counterpart written
in Python" statement. If the C extension calls from C &lt;em&gt;back&lt;/em&gt; into
Python code, and that Python code &lt;em&gt;itself&lt;/em&gt; is cooperative, than
thanks to the magic of greenlet being able to switch C call stacks,
the whole thing winds up cooperative. The call from C into Python
could either be explicit, or implicit (for example, sorting or
hashing values can call arbitrary Python code in an object's
&lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;__hash__&lt;/tt&gt; methods).&lt;/p&gt;
&lt;p&gt;A few good examples of CPU accelerator extensions that can operate
cooperatively are the &lt;a class="reference external" href="https://btrees.readthedocs.io/en/latest/"&gt;BTrees&lt;/a&gt; (sorted dictionaries) and
&lt;a class="reference external" href="https://zopeinterface.readthedocs.io/en/latest/"&gt;zope.interface&lt;/a&gt; (fast adapter and component registration and
lookup) libraries. These libraries need to sort or hash objects.
They are commonly used with objects that are &lt;a class="reference external" href="https://persistent.readthedocs.io/en/latest/"&gt;persistent&lt;/a&gt; and
stored in a &lt;a class="reference external" href="http://www.zodb.org/en/latest/"&gt;ZODB&lt;/a&gt; database, so sorting or hashing may need to
transparently fetch the object from the database. ZODB is
implemented in pure Python, so the original C operation winds up
being cooperative. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id4" id="id2"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="synergism-matters"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id7"&gt;Synergism Matters&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The point of having one greenlet periodically print dots in those
examples while the other greenlet either cooperated or not was to show
why cooperative code matters. If one greenlet is not cooperating, no
other greenlet can make any forward progress (print dots). This might
stop other greenlets for a noticeably long time (like the CPU-bound
example of counting to 268,435,456, or it might be so quick it's
difficult for a human to catch (like resolving a hostname). In either
case, it can be a problem, especially if you're trying to scale a
program such as a server to handle more connections. Every yield lost
potentially decreases overall velocity.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="avoiding-blocking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id8"&gt;Avoiding Blocking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Above, we saw some examples of non-cooperative code. There were some
clear transformations to go through to make code cooperative:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Switch from standard library API to gevent API.&lt;/p&gt;
&lt;p&gt;Change imports to import from gevent. E.g., &lt;tt class="docutils literal"&gt;import time&lt;/tt&gt;⟹&lt;tt class="docutils literal"&gt;from gevent import time&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;import socket&lt;/tt&gt;⟹&lt;tt class="docutils literal"&gt;from gevent
import socket&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://www.gevent.org/intro.html#monkey-patching"&gt;Monkey patch&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In code that's designed to be portable to a non-gevent system, you
can use the standard library API and have gevent automatically make
it cooperative by monkey patching. Many third-party libraries such
as &lt;a class="reference external" href="http://requests.readthedocs.io/en/master/"&gt;requests&lt;/a&gt; that
use only the Python standard API can become cooperative this way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Break up CPU intensive loops by explicitly yielding&lt;/p&gt;
&lt;p&gt;If you know you'll have a CPU intensive loop, like in the example,
you can periodically explicitly yield by calling &lt;tt class="docutils literal"&gt;gevent.sleep()&lt;/tt&gt;
in the loop. Beginning in gevent 1.3, this attempts to automatically
balance the amount of time CPU intensive tasks get while still
allowing other greenlets to perform IO.&lt;/p&gt;
&lt;p&gt;gevent also provides a &lt;a class="reference external" href="http://www.gevent.org/api/gevent.hub.html#gevent.hub.Hub.threadpool"&gt;threadpool&lt;/a&gt; you can use to offload CPU
intensive tasks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="diagnosing-blocking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id9"&gt;Diagnosing Blocking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You may have done all of that and are still not sure if you're
blocking the hub or not: maybe a colleague added a new C extension to
your project and you don't know its internals, or maybe there's an
inner loop somewhere that &lt;em&gt;might&lt;/em&gt; be CPU intensive and you're just not
sure.&lt;/p&gt;
&lt;p&gt;That's where &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html"&gt;my next post&lt;/a&gt;
comes in. We'll discuss how to use gevent's monitoring facilities to
find greenlets that aren't cooperating.&lt;/p&gt;
&lt;!-- the blocking problem
how to know if blocking happens?
    tracing
        what it is
        how we use it
previous support
  github comment and links
implementation in gevent 1.3 --&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="id3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Unless of course you &lt;a class="reference external" href="http://www.gevent.org/intro.html#monkey-patching"&gt;monkey patch&lt;/a&gt;, but that's another
post. Or at least another section of this post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="id4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;At least when the system is monkey patched. But that's another
post. Or at least another section of this post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>1.3</category><category>blocking</category><category>debugging</category><category>gevent</category><category>greenlet</category><category>hub</category><category>implementation</category><category>python</category><category>series</category><category>tracing</category><guid>https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html</guid><pubDate>Tue, 26 Jun 2018 14:36:00 GMT</pubDate></item></channel></rss>