<?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>hack.write()</title><link>http://hackwrite.com/</link><description>Writing about writing about development.</description><atom:link href="http://hackwrite.com/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sat, 08 Sep 2018 02:15:08 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Learn About Python Decorators by Writing a Function Dispatcher</title><link>http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;div tabindex="-1" id="notebook" class="border-box-sizing"&gt;
    &lt;div class="container" id="notebook-container"&gt;

&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Python decorators transform how functions work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@this_is_a_decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;something&lt;/span&gt;

&lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;span class="sd"&gt;&amp;gt;&amp;gt;&amp;gt; some_function()&lt;/span&gt;
&lt;span class="sd"&gt;Something else!&lt;/span&gt;
&lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Between the core language and the standard library,
there are several decorators that come with Python.
Also, many popular frameworks provide decorators
to help eliminate boilerplate 
and make building applications faster and easier.&lt;/p&gt;
&lt;p&gt;Decorators are easier to use than to understand.&lt;/p&gt;
&lt;p&gt;This article will try to help you understand 
how decorators work and
how to write them.
To do that,
we'll write a basic implementation of a dispatch function,
which will conditionally call function implementations
based on the value of an argument.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="A-Problem-in-Need-of-Decorators"&gt;A Problem in Need of Decorators&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#A-Problem-in-Need-of-Decorators"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;How often have you written code that looks something like this?&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;if_chain_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"orange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"yellow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# some default large block of code&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Most of us have written some version of that more than we'd like to admit.
Some languages even have a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch"&gt;special mechanism (usually called &lt;code&gt;switch&lt;/code&gt;) for doing it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But it has problems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As the number of cases increases, it becomes hard to read.&lt;/li&gt;
&lt;li&gt;The code blocks in each case will likely evolve independently, 
some getting refactored into clean functions, 
some not, 
and some being a mix of inline code and custom function calls.&lt;/li&gt;
&lt;li&gt;New cases have to be added explicitly in this function. 
This precludes pluggable cases from external modules, 
and also just adds mental load.&lt;/li&gt;
&lt;li&gt;If the cases are anything other than simple comparisons,
the whole thing quickly becomes difficult to reason about.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To solve these problems, 
making the code more readable and easier to extend, 
we're going to look at function dispatching.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="A-little-bit-about-function-dispatching"&gt;A little bit about function dispatching&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#A-little-bit-about-function-dispatching"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Conventionally, function dispatching is related to the &lt;em&gt;type&lt;/em&gt; of the argument.
That is, function dispatching is a mechanism for doing different things,
depending on whether you pass in an &lt;code&gt;int&lt;/code&gt; or a &lt;code&gt;str&lt;/code&gt; or a &lt;code&gt;list&lt;/code&gt; or whatever.&lt;/p&gt;
&lt;p&gt;Python is &lt;a href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/"&gt;dynamically typed&lt;/a&gt;,
so you don't have to specify that a function only accepts some specific type as an argument.
But if those different types have to be handled differently,
you might end up with code that looks eerily similar to the code above.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;depends_on_type&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="k"&gt;if&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&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;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# large block of code&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# some default large block of code&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;This has all the same problems mentioned above.
But, unlike the first example, Python has a solution to this already:&lt;br&gt;
&lt;a href="https://docs.python.org/3/library/functools.html#functools.singledispatch"&gt;&lt;code&gt;@functools.singledispatch&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a &lt;a href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/"&gt;decorator&lt;/a&gt; 
which transforms a function into a single-dispatch generic function. 
You then register other functions against it, 
specifying a type of object (that is, a class name).
When the function is called, it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;looks up the type of the first argument&lt;/li&gt;
&lt;li&gt;checks its registry for that type&lt;/li&gt;
&lt;li&gt;executes the function registered for that type&lt;/li&gt;
&lt;li&gt;if the type wasn't registered, the original function is executed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If this sounds complicated, don't worry. Using &lt;code&gt;singledispatch&lt;/code&gt; is simpler than explaining it.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [1]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;

&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch_on_type&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="c1"&gt;# some default logic&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"I am the default implementation."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
&lt;span class="nd"&gt;@dispatch_on_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&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="c1"&gt;# some stringy logic&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{x}&lt;/span&gt;&lt;span class="s2"&gt;' is a string."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
&lt;span class="nd"&gt;@dispatch_on_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&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="c1"&gt;# some integer logic&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{x}&lt;/span&gt;&lt;span class="s2"&gt; is an integer."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
&lt;span class="nd"&gt;@dispatch_on_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&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="c1"&gt;# some list logic&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{x}&lt;/span&gt;&lt;span class="s2"&gt; is a list."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [2]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dispatch_on_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;I am the default implementation.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [3]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dispatch_on_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"STRING"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;'STRING' is a string.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [4]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dispatch_on_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1337&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;1337 is an integer.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [5]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;dispatch_on_type&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&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="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;[1, 3, 3, 7] is a list.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;You can do all sorts of cool things with &lt;code&gt;functools.singledispatch&lt;/code&gt;,
but it doesn't solve the problem in the code at the top of the page.
For that, 
we're going to create a decorator similar to &lt;code&gt;singledispatch&lt;/code&gt;
that dispatches based on the &lt;em&gt;value&lt;/em&gt; of the first argument
instead of the type.&lt;/p&gt;
&lt;p&gt;Along the way we'll learn more about how decorators work.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Writing-Decorators"&gt;Writing Decorators&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Writing-Decorators"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;@&lt;/code&gt; decorator syntax is &lt;a href="https://en.wikipedia.org/wiki/Syntactic_sugar"&gt;syntactic sugar&lt;/a&gt;
that covers &lt;a href="https://twitter.com/adammichaelwood/status/1035338321877037056"&gt;passing a function to another function and then returning a function&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What?&lt;/p&gt;
&lt;p&gt;Again, this is easier to show than to explain.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [6]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;


&lt;span class="c1"&gt;# The sweet decorator way...&lt;/span&gt;
&lt;span class="nd"&gt;@a_decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Which has exactly the same effect as...&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;some_other_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Some other function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
&lt;span class="n"&gt;some_other_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_other_function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;A decorator is just a function that returns a function.&lt;/p&gt;
&lt;p&gt;When used with the &lt;code&gt;@&lt;/code&gt; syntax,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the decorator function is called, with the decorated function passed as an argument;&lt;/li&gt;
&lt;li&gt;the return value of the decorator function is assigned to the same name as the decorated function.&lt;/li&gt;
&lt;li&gt;When you call the decorated function, you are &lt;em&gt;actually&lt;/em&gt; calling the function that was returned by the decorator
(which may or may not call the original function's code).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But the above example returned the original function without altering it.
The point of decorators is to return something other than the original function,
in order to transform the function in some way.&lt;/p&gt;
&lt;p&gt;To do this, 
we usually define another function inside the decorator,
and then return that.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [7]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;never_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&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;kw&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="n"&gt;func&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;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;if&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;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
    
&lt;span class="nd"&gt;@never_two&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&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="k"&gt;return&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;y&lt;/span&gt;
    
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [8]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[8]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;3&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;The &lt;code&gt;wrapper&lt;/code&gt; function is defined inside &lt;code&gt;never_two&lt;/code&gt;, 
but it is not executed when &lt;code&gt;never_two&lt;/code&gt; is executed
(which happens at the line where &lt;code&gt;@never_two&lt;/code&gt; appears).
Notice — it isn't called anywhere.
(That is, you don't see anything like &lt;code&gt;wrapper(1,1)&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Instead, the &lt;code&gt;wrapper&lt;/code&gt; function is returned by &lt;code&gt;@never_two&lt;/code&gt;,
and assigned to the name &lt;code&gt;add&lt;/code&gt;.
Meanwhile, the code in the original &lt;code&gt;add&lt;/code&gt; definition
is &lt;em&gt;inside&lt;/em&gt; &lt;code&gt;wrapper&lt;/code&gt;, where it is called &lt;code&gt;func&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;add(1,1)&lt;/code&gt; is called:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The code defined in &lt;code&gt;wrapper&lt;/code&gt; is executed, because it was assigned to &lt;code&gt;add&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The arguments passed into &lt;code&gt;add&lt;/code&gt; (the two &lt;code&gt;1&lt;/code&gt;s) are passed on to &lt;code&gt;func&lt;/code&gt; 
when it is called at &lt;code&gt;x = func(*args, **kw)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The code originally defined at &lt;code&gt;add&lt;/code&gt; (&lt;code&gt;return x + y&lt;/code&gt;) is executed,
because that code was assigned to &lt;code&gt;func&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The output of &lt;code&gt;func&lt;/code&gt; (the original &lt;code&gt;add&lt;/code&gt;) is compared to &lt;code&gt;2&lt;/code&gt;, 
and altered accordingly.&lt;/li&gt;
&lt;li&gt;The code defined under &lt;code&gt;wrapper&lt;/code&gt; (currently being called &lt;code&gt;add&lt;/code&gt;) returns &lt;code&gt;3&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Two points might be helpful here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Think of a function as just everything from the parenthesis onward, excluding the name.
Once you think of a function as a block of code that accepts arguments,
which can be assigned to any name,
things get a little easier to understand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;(*args, **kw)&lt;/code&gt; is a way to 
collect, pass on, and then unpack 
all the positional and keyword arguments.
A full treatment is beyond the scope of this article.
For now, just notice that whatever is passed into &lt;code&gt;wrapper&lt;/code&gt;
is simply passed on to &lt;code&gt;func&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Writing-a-Dispatch-Decorator"&gt;Writing a Dispatch Decorator&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Writing-a-Dispatch-Decorator"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Let's look at the syntax of &lt;code&gt;functools.singledispatch&lt;/code&gt; again,
and think about what we need to do to emulate it for values instead of types.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [9]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch_on_type&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"I am the default implementation."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
&lt;span class="nd"&gt;@dispatch_on_type&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{x}&lt;/span&gt;&lt;span class="s2"&gt;' is a string."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Decorators-that-Return-Decorators"&gt;Decorators that Return Decorators&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Decorators-that-Return-Decorators"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Notice that we actually have &lt;strong&gt;two&lt;/strong&gt; decorators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;functools.singledispatch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dispatch_on_type.register&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means inside &lt;code&gt;singledispatch&lt;/code&gt;, 
the decorated function (in this case, &lt;code&gt;dispatch_on_type&lt;/code&gt;)
is being assigned an additional attribute, 
&lt;code&gt;.register&lt;/code&gt;, which is also a decorator function.&lt;/p&gt;
&lt;p&gt;That might look something like:&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [10]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner_wrapper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        
            &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Inner wrapper."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inner_wrapper&lt;/span&gt;
    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The wrapper function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decorator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inner_decorator&lt;/span&gt;
    
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="nd"&gt;@outer_decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Original a_function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# This will never execute.&lt;/span&gt;

&lt;span class="nd"&gt;@a_function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;another_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Original another_function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# This will never execute.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [11]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;a_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;The wrapper function.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [12]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;another_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;Inner wrapper.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Unpacking that a bit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;outer_decorator&lt;/code&gt; defines two functions,&lt;code&gt;inner_decorator&lt;/code&gt; and &lt;code&gt;wrapper&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wrapper&lt;/code&gt; is returned by &lt;code&gt;outer_decorator&lt;/code&gt;, so it will be executed when &lt;code&gt;a_function&lt;/code&gt; is called&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inner_decorator&lt;/code&gt; is assigned as an attribute of &lt;code&gt;wrapper&lt;/code&gt;, so &lt;code&gt;a_function.inner_decorator&lt;/code&gt; becomes a usable decorator&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inner_decorator&lt;/code&gt; defines &lt;code&gt;inner_wrapper&lt;/code&gt; and returns it, so it will be executed when &lt;code&gt;another_function&lt;/code&gt; is called&lt;/li&gt;
&lt;/ul&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Decorators-with-Arguments"&gt;Decorators with Arguments&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Decorators-with-Arguments"&gt;¶&lt;/a&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;You may have noticed that up until now,
the decorators created in this article did not included parentheses or arguments
when attached to functions.
This is because the decorated function is actually passed
as the only argument to the function call.&lt;/p&gt;
&lt;p&gt;But when registering functions against types,
&lt;code&gt;singledispatch&lt;/code&gt; included an argument.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [13]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatched&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

&lt;span class="nd"&gt;@dispatched&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Incredibly, the way to achieve this is to nest &lt;em&gt;yet another function&lt;/em&gt; into the decorator.&lt;/p&gt;
&lt;p&gt;That is because, really, &lt;code&gt;register&lt;/code&gt; isn't a decorator. 
Instead, &lt;strong&gt;&lt;code&gt;register&lt;/code&gt; is a function which returns a decorator when passed an argument.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let's take our previous example and expand it to include this idea.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [14]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;outer_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;faux_decorator_w_arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;actual_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            
            &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;inner_wrapper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        
                &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"Inner wrapper. arg was: &lt;/span&gt;&lt;span class="si"&gt;{arg}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;inner_wrapper&lt;/span&gt;
        
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual_decorator&lt;/span&gt;
    
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"The wrapper function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decorator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;faux_decorator_w_arg&lt;/span&gt;
    
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="nd"&gt;@outer_decorator&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;a_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Original a_function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# This will never execute.&lt;/span&gt;

&lt;span class="nd"&gt;@a_function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"decorator_argument"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;another_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Original another_function."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# This will never execute.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [15]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;a_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;The wrapper function.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [16]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;another_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;Inner wrapper. arg was: decorator_argument
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Putting-it-Together"&gt;Putting it Together&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Putting-it-Together"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;So now we know how to create decorators that return decorators and accepts arguments. 
With this,
plus a dictionary that maps registered values to functions,
we can create a dispatch on value decorator.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [17]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch_on_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;span class="sd"&gt;    Value-dispatch function decorator.&lt;/span&gt;
&lt;span class="sd"&gt;    &lt;/span&gt;
&lt;span class="sd"&gt;    Transforms a function into a value-dispatch function,&lt;/span&gt;
&lt;span class="sd"&gt;    which can have different behaviors based on the value of the first argument.&lt;/span&gt;
&lt;span class="sd"&gt;    """&lt;/span&gt;
    
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&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;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;registry&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;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&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;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
       
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;register&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;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        
        &lt;span class="n"&gt;registry&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;
        
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&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;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;register&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [18]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@dispatch_on_value&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;react_to_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Everything's fine."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    

&lt;span class="nd"&gt;@react_to_status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Red status is probably bad.&lt;/span&gt;
    &lt;span class="c1"&gt;# So we need lots of complicated code here to deal with it.&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Status is red."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [19]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;react_to_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;Status is red.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;There are a few things here which might not be obvious.
So let's take a closer look.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&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;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;registry&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;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is called by &lt;code&gt;wrapper&lt;/code&gt;, and is the mechanism that determines which registered function is executed.
It looks in the registry and returns the appropriate function (without executing it).
If the value isn't registered,
this will raise a KeyError.
The &lt;code&gt;except&lt;/code&gt; block catches that error
and returns the original function.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&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;span class="n"&gt;func&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;func&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;register&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;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;registry&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This acts as both the &lt;code&gt;faux_decorator&lt;/code&gt; and the &lt;code&gt;actual_decorator&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It can be called with one or two positional arguments;
if the second one is omitted, it is set to &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At &lt;code&gt;@react_to_status.register("red")&lt;/code&gt;, it is being called with only the &lt;code&gt;value&lt;/code&gt; argument.
This causes the &lt;code&gt;lambda expression&lt;/code&gt; to be returned, with &lt;code&gt;value&lt;/code&gt; already interpreted.
(That is, the return value is &lt;code&gt;lambda f: register("red", f)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This is the same as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;func&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;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;actual_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;register&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;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="n"&gt;decorator&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But the lambda expression is a bit easier to read, once you know what it is doing.&lt;/p&gt;
&lt;p&gt;This returned lambda function is then the actual decorator,
and is executed with the wrapped function as its one argument.
The function argument is them passed to &lt;code&gt;register&lt;/code&gt;,
along with the &lt;code&gt;value&lt;/code&gt; that got set when the lambda was created.&lt;/p&gt;
&lt;p&gt;Now &lt;code&gt;register&lt;/code&gt; runs again,
but this time it has both arguments.
The &lt;code&gt;if func is None&lt;/code&gt; is skipped,
and the function is added to the registry with &lt;code&gt;value&lt;/code&gt; as the key.
The function is returned back to the point when the &lt;code&gt;register&lt;/code&gt; decorator was called,
but it gets assigned to the name &lt;code&gt;_&lt;/code&gt;, 
because we never need to call it directly.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&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;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the function that actually gets executed when &lt;code&gt;react_to_status&lt;/code&gt; is called.
It calls &lt;code&gt;dispatch&lt;/code&gt; with the first argument (&lt;code&gt;arg[0]&lt;/code&gt;),
which return the appropriate function.
The returned function is immediatly called, with &lt;code&gt;*args, **kw&lt;/code&gt; passed in.
Any output from the function is returned to the caller of &lt;code&gt;react_to_status&lt;/code&gt;,
which completes the entire dispatch process.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Going-Further"&gt;Going Further&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Going-Further"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;This tutorial looked at value dispatch in order to dig into how decorators work. 
It does not provide a complete implementation for a practical value dispatch decorator.&lt;/p&gt;
&lt;p&gt;For example,
in practice you'd probably want value dispatch to include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;values within a range&lt;/li&gt;
&lt;li&gt;values in a collection&lt;/li&gt;
&lt;li&gt;values matching a regular expression&lt;/li&gt;
&lt;li&gt;values meeting criteria defined in a fitness or sorting function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And we didn't even talk about additional &lt;code&gt;functools&lt;/code&gt; features 
that &lt;a href="https://docs.python.org/3/library/functools.html#functools.wraps"&gt;help sort out introspection&lt;/a&gt;
or &lt;a href="https://hynek.me/articles/decorators/"&gt;dealing with the many problems created by decorators&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a more complete, production ready implementation of this idea, 
see &lt;a href="https://github.com/minimind/dispatch-on-value-for-python"&gt;Dispatch on Value by minimind&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;hr&gt;
&lt;h2 id="Credits"&gt;Credits&lt;a class="anchor-link" href="http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/#Credits"&gt;¶&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;The final form of &lt;code&gt;dispatch_on_value&lt;/code&gt; was based heavily on 
the &lt;a href="https://github.com/pybee/ouroboros"&gt;Ouroboros&lt;/a&gt; implementation of &lt;a href="https://github.com/pybee/ouroboros/blob/master/ouroboros/functools.py"&gt;&lt;code&gt;functools.singledispatch&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
</description><guid>http://hackwrite.com/posts/learn-about-python-decorators-by-writing-a-function-dispatcher/</guid><pubDate>Sat, 08 Sep 2018 01:39:32 GMT</pubDate></item><item><title>Object-oriented vs. Functional Modelling of Musical Arithmetic in Python</title><link>http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;div tabindex="-1" id="notebook" class="border-box-sizing"&gt;
    &lt;div class="container" id="notebook-container"&gt;

&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h1 id="Classical-vs.-Functional-Modelling-of-Musical-Arithmetic-in-Python"&gt;Classical vs. Functional Modelling of Musical Arithmetic in Python&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Classical-vs.-Functional-Modelling-of-Musical-Arithmetic-in-Python"&gt;¶&lt;/a&gt;&lt;/h1&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;I'm currently building a music theory library in Python, called &lt;a href="https://github.com/OphisMusic/ophis"&gt;Ophis&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [1]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;ophis&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;This is an attempt create a utility that "understands" music theory and can manipulate music information, to be used as a base for other applications.  This would be handy for all sort of things, from music theory educational apps to AI composition.&lt;/p&gt;
&lt;p&gt;In this notebook, we'll look at how I originally implemented basic musical arithmetic in Ophis, the problems with that approach, and why I am moving from a classical to a functional design.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="A-Classical-OOP-Design"&gt;A Classical OOP Design&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#A-Classical-OOP-Design"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;My first approach in implementing this was classically object oriented, and influenced by an essentially Platonic ontology.&lt;/p&gt;
&lt;p&gt;The idea was that musical building blocks would be, as much as possible, similar to integers.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [2]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# A `Chroma` is the *idea* of a note letter name &lt;/span&gt;
&lt;span class="c1"&gt;#     Example: "A" or "D FLAT"&lt;/span&gt;
&lt;span class="c1"&gt;# 35 chromae are initialized to constants on load, &lt;/span&gt;
&lt;span class="c1"&gt;#   representing all 7 letter names, &lt;/span&gt;
&lt;span class="c1"&gt;#   with sharps, flats, double sharps, and double flats.&lt;/span&gt;

&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wcs&lt;/span&gt; &lt;span class="c1"&gt;# Western Chroma Set, &lt;/span&gt;
          &lt;span class="c1"&gt;# the complete list of all initialized chromae&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[2]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;{BDUBFLAT,
 GDUBSHARP,
 BFLAT,
 DDUBSHARP,
 F,
 DFLAT,
 D,
 ADUBFLAT,
 AFLAT,
 G,
 CFLAT,
 C,
 FDUBFLAT,
 GDUBFLAT,
 B,
 GSHARP,
 BSHARP,
 GFLAT,
 ASHARP,
 FDUBSHARP,
 CDUBFLAT,
 DDUBFLAT,
 E,
 EDUBFLAT,
 CSHARP,
 EFLAT,
 EDUBSHARP,
 FFLAT,
 A,
 BDUBSHARP,
 CDUBSHARP,
 DSHARP,
 ADUBSHARP,
 ESHARP,
 FSHARP}&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;One of the main ideas here is that there is one and only one representation of the idea of &lt;em&gt;C SHARP&lt;/em&gt; or &lt;em&gt;F NATURAL&lt;/em&gt;
. Moreover, the chromae can be inspected, and know how to represent themselves.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [3]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unicode&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[3]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'F♯'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [4]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ascii&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[4]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'F#'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [5]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[5]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'F'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Chromae also carry all the logic needed for musical manipulation and mathematical representation.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [6]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[6]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;6&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [7]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[7]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;G&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [8]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;diminish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[8]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;F&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;A &lt;code&gt;Pitch&lt;/code&gt; is a &lt;code&gt;Chroma&lt;/code&gt; with an octave designation. Using the special &lt;code&gt;__call__&lt;/code&gt; method on &lt;code&gt;Chroma&lt;/code&gt;, and the &lt;code&gt;__repr__&lt;/code&gt; method on &lt;code&gt;Pitch&lt;/code&gt;, I was able to make their interactive representation is intuitive.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# in Chroma class&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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;octave&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Pitch&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;octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# in Pitch class:&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__repr__&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="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="n"&gt;chroma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&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;octave&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;")"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [9]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# The "standard Python" way to create a pitch. &lt;/span&gt;
&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GFLAT&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[9]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;GFLAT(2)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [11]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# The Ophis canonical way.&lt;/span&gt;

&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GFLAT&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[11]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;GFLAT(2)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;&lt;code&gt;Intervals&lt;/code&gt; (without octaves) and &lt;code&gt;QualifiedIntervals&lt;/code&gt; (with octaves) have a similar relationship to each other as &lt;code&gt;Chroma&lt;/code&gt; and &lt;code&gt;Pitch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Rather than initializing every possible musical interval, the qualities (major, minor, perfect, augmented, diminished) are initialized and callable, to create an intuitive API.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [12]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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="c1"&gt;# A Major second.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[12]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;Major(2)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [13]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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="c1"&gt;# A Perfect fourth, plus 2 octaves.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[13]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;Perfect(4)^2&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Function caching is used to ensure that only one of any interval is created. (Some experimental benchmarking showed that this would matter in large scores.)&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [14]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;augmented&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[14]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;True&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;And, of course, you can use both types of intervals to manipulate chromae and pitches.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [15]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[15]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;A&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [16]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[16]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;E(3)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [17]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FSHARP&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[17]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;GSHARP(3)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;All this lets you do complicated musical manipulation and representation.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [18]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FFLAT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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="n"&gt;diminish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unicode&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[18]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'B♭'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Obviously, all this is only the beginning of what is needed for a music theory library. But it &lt;em&gt;is&lt;/em&gt; a beginning. The next submodule will build up &lt;code&gt;Duration&lt;/code&gt; and &lt;code&gt;TimeSignature&lt;/code&gt;, leading to the creation of &lt;code&gt;Measure&lt;/code&gt; and eventually &lt;code&gt;Score&lt;/code&gt;. My current plan is to use &lt;code&gt;pandas.DataFrame&lt;/code&gt; for multi-voice scores, as that would allow cross-voice analysis in a way that multi-dimensional lists would not.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Problems-Appear"&gt;Problems Appear&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Problems-Appear"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;So that's great, but...&lt;/p&gt;
&lt;p&gt;I can't but help wonder if some of this is overwrought.&lt;/p&gt;
&lt;p&gt;A number of interrelated concerns occured to me while working on this implementation.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Logic-is-hard-to-reason-about"&gt;Logic is hard to reason about&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Logic-is-hard-to-reason-about"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The math of moving from note to note is riddled with off-by-one and modulo arithmetic problems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An interval representing no change (from a note to itself) is called a &lt;em&gt;unison&lt;/em&gt;, represented with a 1. A difference of one step is called a &lt;em&gt;second&lt;/em&gt;, and so on.&lt;/li&gt;
&lt;li&gt;The first scale degree is 1. (Not zero indexed.)&lt;/li&gt;
&lt;li&gt;We frequently think about scales as having eight notes, but in reality they only have seven. When this is zero indexed, the notes go from 0-6. This is fine for arithmetic, but when thinking as a musician it is jarring.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of this difficulty in clear thinking on my part, I often found myself using the guess-and-check method for remembering when to add or subtract a one.&lt;/p&gt;
&lt;p&gt;I wrote rigorous &lt;a href="https://github.com/OphisMusic/ophis/tree/master/tests"&gt;tests&lt;/a&gt; along the way to keep these errors out, so everything ends up fine in the end. However, this made for slow and sometimes demoralizing progress, and I would hate to have to go back and reason about this code after being away from it.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Incorrect-assumptions-about-logical-order"&gt;Incorrect assumptions about logical order&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Incorrect-assumptions-about-logical-order"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;The first attempt to implement basic &lt;code&gt;Chroma&lt;/code&gt; functionality assumed that &lt;code&gt;Interval&lt;/code&gt; — the relationship between two chromae — would depend on &lt;code&gt;Chroma&lt;/code&gt;. It turns out this is exactly backwards. &lt;code&gt;Interval&lt;/code&gt; is logically prior to &lt;code&gt;Chroma&lt;/code&gt;. There is no way to define abstract named pitches without their relationships already existing.&lt;/p&gt;
&lt;p&gt;Practically speaking, discovering this simply meant I had to re-order some code. But this challenged my thinking about what the fundamental building blocks of music theory actually are.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Convoluted-logic-and-utility-data-structures"&gt;Convoluted logic and utility data structures&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Convoluted-logic-and-utility-data-structures"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Here's an example, the &lt;code&gt;augment&lt;/code&gt; method from the &lt;code&gt;Chroma&lt;/code&gt; class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;augment&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;magnitude&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;modifier_preference&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sharp"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;"""Return a chroma higher than the one given.&lt;/span&gt;

&lt;span class="sd"&gt;    Args:&lt;/span&gt;
&lt;span class="sd"&gt;        magnitude (:obj:`int`, :obj:`Interval`,&lt;/span&gt;
&lt;span class="sd"&gt;                   or obj with an ``int`` value; optional): &lt;/span&gt;
&lt;span class="sd"&gt;            the distance to augment by. &lt;/span&gt;
&lt;span class="sd"&gt;            Integer values are interpreted as half steps. &lt;/span&gt;
&lt;span class="sd"&gt;            Defaults to 1.&lt;/span&gt;
&lt;span class="sd"&gt;        modifier_preference (:obj:`str`, &lt;/span&gt;
&lt;span class="sd"&gt;                             ``'sharp'`` or ``'flat'``;&lt;/span&gt;
&lt;span class="sd"&gt;                             optional)&lt;/span&gt;
&lt;span class="sd"&gt;            Defaults to ``'sharp'``. &lt;/span&gt;

&lt;span class="sd"&gt;    Examples:&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; C.augment()&lt;/span&gt;
&lt;span class="sd"&gt;        CSHARP&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; C.augment(1, 'flat')&lt;/span&gt;
&lt;span class="sd"&gt;        DFLAT&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; C.augment(minor(3))&lt;/span&gt;
&lt;span class="sd"&gt;        EFLAT&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; D.augment(2)&lt;/span&gt;
&lt;span class="sd"&gt;        E&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; E.augment()&lt;/span&gt;
&lt;span class="sd"&gt;        F&lt;/span&gt;

&lt;span class="sd"&gt;        &amp;gt;&amp;gt;&amp;gt; E.augment(2, 'flat')&lt;/span&gt;
&lt;span class="sd"&gt;        GFLAT&lt;/span&gt;
&lt;span class="sd"&gt;    """&lt;/span&gt;

    &lt;span class="n"&gt;value_candidates&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;essential_set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chroma_by_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;int&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="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;magnitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;letter_candidates&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;essential_set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chroma_by_letter&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;base_num&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;magnitude&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;solution&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_candidates&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;letter_candidates&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;solution&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value_candidates&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enharmonic_reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier_preference&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If it isn't obvious, here's what it does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Calculate the integer value of the target &lt;code&gt;Chroma&lt;/code&gt; and find the set of &lt;code&gt;Chroma&lt;/code&gt; objects which have the integer value we're looking for. &lt;/li&gt;
&lt;li&gt;Try:&lt;ul&gt;
&lt;li&gt;Calculate the letter name of the target &lt;code&gt;Chroma&lt;/code&gt; and find the set of &lt;code&gt;Chroma&lt;/code&gt; that have the name value we're looking for.&lt;/li&gt;
&lt;li&gt;Find and return the union of the integer-value set and the note-name value set.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Except:&lt;ul&gt;
&lt;li&gt;Return a member of the integer-value set, basing the selection on some logic (defined elsewhere) that prefers sharps to flats or flats to sharps in certain instances.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This works, but it isn't at all how a musician thinks about this operation. Moreover, it depends on the &lt;code&gt;essential_set&lt;/code&gt;, the collection of all initialized chromae. (Referred to above as &lt;code&gt;wcs&lt;/code&gt;, the Wesern Chroma Set.) It would be bad enough if this was &lt;em&gt;just&lt;/em&gt; used to keep the pool of initialized chromae, so that methods returning &lt;em&gt;C Sharp&lt;/em&gt; always returned the same &lt;em&gt;C Sharp&lt;/em&gt;. But it doesn't just do that. An inordinate amount of musical knowledge and logic crept into the &lt;code&gt;ChromaSet&lt;/code&gt; class that defines the &lt;code&gt;essential_set&lt;/code&gt;. While I'm positive that some of this is due to bad &lt;em&gt;coding&lt;/em&gt; on my part, I think the bulk of it is due to bad &lt;em&gt;conceptualization&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The final problem with this is that it is non-obvious. This code is hard to read and reason about, because it isn't clear what is actually happening.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Fragile-Primitives"&gt;Fragile Primitives&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Fragile-Primitives"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Python doesn't &lt;em&gt;really&lt;/em&gt; allow you to protect object attributes or module-level constants. There are some things you can do to ensure object attributes aren't reassigned accidentally (and I've done them), but (as far as I can tell) module-level constants cannot be protected.&lt;/p&gt;
&lt;p&gt;This is a problem, since the fundamental building blocks of music theory in the current implementation are initialized as constants. The object representing &lt;em&gt;C Sharp&lt;/em&gt; is created and assigned to the name &lt;code&gt;CSHARP&lt;/code&gt;. If that name gets reassigned, &lt;em&gt;you are basically hosed&lt;/em&gt;. This could lead to hard-to-trace errors and frustrating interactive sessions.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Poor-isomorphism-to-numbers"&gt;Poor isomorphism to numbers&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Poor-isomorphism-to-numbers"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;One of the design goals of Ophis is to be able to treat musical concepts as numbers. That's why the arithmetic operators are implemented and everything has an integer value. I wanted it to be easy for math utilities to operate on pitches and intervals. This would enable things like advanced theoretical analysis  and machine learning.&lt;/p&gt;
&lt;p&gt;But, they &lt;em&gt;aren't&lt;/em&gt; numbers. They just aren't.&lt;/p&gt;
&lt;p&gt;You can't (meaningfully) have a Chroma with a float, decimal, or fractional value. This means that &lt;a href="https://en.wikipedia.org/wiki/Microtonal_music"&gt;microtones&lt;/a&gt; are not presently accounted for and will require an extension, the logic of which I can only guess at.&lt;/p&gt;
&lt;p&gt;You also can't meaningfully multiply or divide values. Offhand, I'm not sure why you would want to do so, but I can imagine approaches to musical analysis where it would be needed.&lt;/p&gt;
&lt;p&gt;Further, even with supported integer-based operations, using any standard math tool requires notes from a score to be converted into numbers, manipulated or analyzed, and converted back. There's no direct access to Ophis "primitives" in Numpy, SciKitLearn, or anything else.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;These problems piled up over time as I implemented the basic logic and worked out the implications. Technical debt accumulates through a process of small compromises and justifications. By the time I became aware of the scope of the problem, I had two thoughts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Re-architecting everything would take too long to be worthwhile. I would probably get disheartened and give up.&lt;/li&gt;
&lt;li&gt;I can refactor the internals in the future to make things a bit clearer and cleaner. In the meantime, good documentation would make the code maintainable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, my plan was to just keep moving. But then, thinking about the isomorphism problem, I realized another poorly-mapped isomorphism.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h3 id="Poor-isomorphism-between-Chroma-and-Intervals"&gt;Poor isomorphism between Chroma and Intervals&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Poor-isomorphism-between-Chroma-and-Intervals"&gt;¶&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Or really, no explicit isomorphism at all. And this is a problem because these are really &lt;em&gt;the same thing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I had implemented the &lt;code&gt;Interval&lt;/code&gt; class, and written all the logic for how intervals are inverted, augmented, and diminished. This requires understanding of the interplay between interval distances (third, fourth, sixth) and their qualities (Major, minor, Perfect), and how many half-steps each are. And of course there's that zero-indexing stuff to think about (second = 1, third = 2).&lt;/p&gt;
&lt;p&gt;Then I implemented the &lt;code&gt;Chroma&lt;/code&gt; class, and wrote almost the same logic (but just a bit different) for how pitches are augmented and diminished (pitches aren't inverted). This requires an understanding of the interplay between note letter names (C, D, E), how those letter names map to a zero-indexed numerical representation (C = 0, D = 1, E = 2), and how modifiers like &lt;em&gt;sharp&lt;/em&gt; and &lt;em&gt;flat&lt;/em&gt; affect the total number of halfsteps from C (the origin point in modern music theory).&lt;/p&gt;
&lt;p&gt;But these are, as I said, &lt;em&gt;exactly the same thing&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Every note can be represented as an interval from C. And not only can it be represented that way, but that is exactly how it was already defined. There is no other reasonable way to (numerically) define notes.&lt;/p&gt;
&lt;p&gt;Here's an example in case this isn't clear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;E Natural&lt;/em&gt; is a Major Third away from &lt;em&gt;C&lt;/em&gt;. &lt;/li&gt;
&lt;li&gt;In our zero-indexed representation intervals, a Third is &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A Major Third is &lt;code&gt;4&lt;/code&gt; half-steps. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those two numbers, &lt;code&gt;(2, 4)&lt;/code&gt;, are an integral part of the definition of &lt;em&gt;E Natural&lt;/em&gt;; without them, you can't do any of the manipulation that makes the &lt;code&gt;Chroma&lt;/code&gt; meaningful.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [19]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;2 4
2 4
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Obviously, this holds for every other &lt;code&gt;Chroma&lt;/code&gt; as well.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [20]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;4 7
4 7
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [21]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Augmented&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Augmented&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ASHARP&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;5 10
5 10
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Further, it turns out that these two numbers are the &lt;em&gt;only&lt;/em&gt; things you need to know in order to do any standard musical manipulation you might want to do.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [22]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;g_or_p5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Tuple representing G or a Perfect Fifth&lt;/span&gt;
&lt;span class="n"&gt;e_or_maj3&lt;/span&gt; &lt;span class="o"&gt;=&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;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Tuple representing E or a Major Third&lt;/span&gt;

&lt;span class="c1"&gt;# Add tuples element wise.&lt;/span&gt;
&lt;span class="n"&gt;sum_of_tuples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;g_or_p5&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e_or_maj3&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;g_or_p5&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e_or_maj3&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="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sum_of_tuples&lt;/span&gt; &lt;span class="c1"&gt;# (6,11)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[22]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;(6, 11)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [23]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;g_augmented_by_maj3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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;e_augmented_by_p5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g_augmented_by_maj3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e_augmented_by_p5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;B
B
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [24]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&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="n"&gt;base_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;6 11
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [25]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&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;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Perfect&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="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Major&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;z&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[25]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;Major(7)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [26]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&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;distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;6 11
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;So any chroma and any interval can be represented by a two-tuple, while manipulations originally implemented as methods in different classes can be a unified set of pure functions that accept tuples as arguments.&lt;/p&gt;
&lt;p&gt;Great.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;But two-tuples don't provide all the additional information you need to notate pitches or otherwise make them understandable &lt;em&gt;as music&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So we need some "translation" functions. This still involves a lot of "magic number" coding, but hopefully it can be condensed into a small set of mappings that are easy to reason about.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [27]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;bidict&lt;/span&gt; &lt;span class="c1"&gt;# efficient two-way indexing of dicts&lt;/span&gt;

&lt;span class="n"&gt;primary_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# half steps, scale degree / interval number, &lt;/span&gt;
    &lt;span class="c1"&gt;# interval name, letter name, Perfect? &lt;/span&gt;
    &lt;span class="c1"&gt;#                             (False=Major) &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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"unison"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s2"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;#0&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"second"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s2"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;#1&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&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="s2"&gt;"third"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s2"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;#2&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"fourth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s2"&gt;"F"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;#3&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&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="s2"&gt;"fifth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s2"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;#4&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sixth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="s2"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;#5&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"seventh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;#6&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Split primary map into bidicts for each value.&lt;/span&gt;
&lt;span class="c1"&gt;#     For faster, more sensible referencing.&lt;/span&gt;
&lt;span class="c1"&gt;#     This feels wrong and I need a better way.&lt;/span&gt;
&lt;span class="c1"&gt;#     Maybe something with named tuple...&lt;/span&gt;

&lt;span class="n"&gt;hs_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;(&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;item&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="k"&gt;for&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_map&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;interval_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;(&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;item&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="k"&gt;for&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_map&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;interval_name_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;(&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;item&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="k"&gt;for&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_map&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;name_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;(&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;item&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="k"&gt;for&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_map&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;quality_map&lt;/span&gt; &lt;span class="o"&gt;=&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;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# How to translate between &lt;/span&gt;
&lt;span class="c1"&gt;# diatonic intervals and modified intervals.&lt;/span&gt;
&lt;span class="n"&gt;interval_quality_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="c1"&gt;# Diatonic is Perfect&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="s1"&gt;'double diminished'&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;'diminished'&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="s1"&gt;'Perfect'&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="s1"&gt;'Augmented'&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="s1"&gt;'Double Augmented'&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="c1"&gt;# Diatonic is Major&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="s1"&gt;'diminished'&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;'minor'&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="s1"&gt;'Major'&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="s1"&gt;'Augmented'&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="s1"&gt;'Double Augmented'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;modifiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bidict&lt;/span&gt;&lt;span class="p"&gt;({&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="s1"&gt;'doubleflat'&lt;/span&gt;&lt;span class="p"&gt;,&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="s1"&gt;'flat'&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="s1"&gt;'natural'&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="s1"&gt;'sharp'&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="s1"&gt;'doublesharp'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [28]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;

&lt;span class="c1"&gt;# Single Dispatch:&lt;/span&gt;
&lt;span class="c1"&gt;#     two functions with the same signature.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;#     The type of the first argument determines &lt;/span&gt;
&lt;span class="c1"&gt;#     which function is executed.&lt;/span&gt;
&lt;span class="c1"&gt;#     This way, if a tuple is passed in, &lt;/span&gt;
&lt;span class="c1"&gt;#         a string is returned,&lt;/span&gt;
&lt;span class="c1"&gt;#     and if a string is passed in, &lt;/span&gt;
&lt;span class="c1"&gt;#         a tuple is returned.&lt;/span&gt;

&lt;span class="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chroma&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@chroma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xy&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name_map&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;modifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modifiers&lt;/span&gt;&lt;span class="p"&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;hs_map&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@chroma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifier&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="n"&gt;name_map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;mod_diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modifiers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&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;hs_map&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;mod_diff&lt;/span&gt;
    &lt;span class="k"&gt;return&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="nd"&gt;@functools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interval&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@interval&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xy&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interval_name_map&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;q_mod&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;hs_map&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;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interval_quality_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;quality_map&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;q_mod&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&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;q&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="nd"&gt;@interval&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# quality, number&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;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;is_perfect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quality_map&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;q_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;interval_quality_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;is_perfect&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&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;hs_map&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="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;q_mod&lt;/span&gt;
    &lt;span class="k"&gt;return&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;augment&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;zip&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diminish&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="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&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;b&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="k"&gt;for&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="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [29]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;chroma&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;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[29]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'C natural'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [30]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;interval&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;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[30]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'diminished third'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [31]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'D'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'sharp'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[31]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;(1, 3)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [32]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Major'&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;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[32]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;(2, 4)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [33]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chroma&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;span class="s1"&gt;'sharp'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'minor'&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;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[33]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;'E natural'&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [34]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;timeit&lt;/span&gt; chroma(
    &lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chroma&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;span class="s1"&gt;'sharp'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'minor'&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="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;22.3 µs ± 1.4 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [35]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CSHARP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor&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;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[35]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;E&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [36]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;timeit&lt;/span&gt; ophis.CSHARP.augment(ophis.minor(3))
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_stream output_stdout output_text"&gt;
&lt;pre&gt;101 µs ± 3.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;A functional approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;simplifies the math and logic&lt;/li&gt;
&lt;li&gt;preserves important isomorphisms&lt;/li&gt;
&lt;li&gt;requires much less code&lt;/li&gt;
&lt;li&gt;executes much faster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The only downside is that the API for interactive use is a little less elegant, but not so much as to be a problem.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Where-to-Go-From-Here"&gt;Where to Go From Here&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Where-to-Go-From-Here"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;The quick functional implementation demonstrated here doesn't include all the things that the OO approach currently has.&lt;/p&gt;
&lt;p&gt;Foremost, this version needs to include modulo arithmetic.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [37]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;augment&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;),(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# Should be (0,0), in musical logic.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[37]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;(8, 12)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [38]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# This result is meaningless.&lt;/span&gt;
&lt;span class="n"&gt;chroma&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# KeyError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_text output_error"&gt;
&lt;pre&gt;
&lt;span class="ansi-red-fg"&gt;---------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="ansi-red-fg"&gt;KeyError&lt;/span&gt;                                  Traceback (most recent call last)
&lt;span class="ansi-green-fg"&gt;&amp;lt;ipython-input-38-114c113c80ec&amp;gt;&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;()&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;      1&lt;/span&gt; &lt;span class="ansi-red-fg"&gt;# This result is meaningless.&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;----&amp;gt; 2&lt;/span&gt;&lt;span class="ansi-red-fg"&gt; &lt;/span&gt;chroma&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;8&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;12&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;

&lt;span class="ansi-green-fg"&gt;/Users/adamwood/amwenv/lib/python3.5/functools.py&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;wrapper&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(*args, **kw)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    741&lt;/span&gt; 
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    742&lt;/span&gt;     &lt;span class="ansi-green-fg"&gt;def&lt;/span&gt; wrapper&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;*&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;**&lt;/span&gt;kw&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;:&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;--&amp;gt; 743&lt;/span&gt;&lt;span class="ansi-red-fg"&gt;         &lt;/span&gt;&lt;span class="ansi-green-fg"&gt;return&lt;/span&gt; dispatch&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;0&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;__class__&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;*&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;**&lt;/span&gt;kw&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    744&lt;/span&gt; 
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    745&lt;/span&gt;     registry&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;object&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; func

&lt;span class="ansi-green-fg"&gt;&amp;lt;ipython-input-28-f2a6c2b4cc47&amp;gt;&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;_&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(xy)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     13&lt;/span&gt; &lt;span class="ansi-green-fg"&gt;def&lt;/span&gt; _&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;xy&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;:&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     14&lt;/span&gt;     x&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt;y &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; xy
&lt;span class="ansi-green-fg"&gt;---&amp;gt; 15&lt;/span&gt;&lt;span class="ansi-red-fg"&gt;     &lt;/span&gt;name &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; name_map&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;x&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     16&lt;/span&gt;     modifier &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; modifiers&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;y &lt;span class="ansi-blue-fg"&gt;-&lt;/span&gt; hs_map&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;x&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     17&lt;/span&gt;     &lt;span class="ansi-green-fg"&gt;return&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;" "&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;join&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;name&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; modifier&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;

&lt;span class="ansi-green-fg"&gt;/Users/adamwood/amwenv/lib/python3.5/site-packages/bidict/_common.py&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;proxy&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(self, *args)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     94&lt;/span&gt;         attr &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; getattr&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;self&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; attrname&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     95&lt;/span&gt;         meth &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; getattr&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;attr&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; methodname&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;---&amp;gt; 96&lt;/span&gt;&lt;span class="ansi-red-fg"&gt;         &lt;/span&gt;&lt;span class="ansi-green-fg"&gt;return&lt;/span&gt; meth&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;*&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     97&lt;/span&gt;     proxy&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;__name__ &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; methodname
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     98&lt;/span&gt;     proxy&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;__doc__ &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; doc &lt;span class="ansi-green-fg"&gt;or&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;"Like dict's ``%s``."&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;%&lt;/span&gt; methodname

&lt;span class="ansi-red-fg"&gt;KeyError&lt;/span&gt;: 8&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Additionally, I need to include octave designations. The arithmetic is almost included for free with the functional approach, but the translation functions don't support it.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [39]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Maj Second (or D) and min 3, with a third term for octave designation&lt;/span&gt;
&lt;span class="n"&gt;augment&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;1&lt;/span&gt;&lt;span class="p"&gt;),&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt output_prompt"&gt;Out[39]:&lt;/div&gt;




&lt;div class="output_text output_subarea output_execute_result"&gt;
&lt;pre&gt;(3, 5, 3)&lt;/pre&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing code_cell rendered"&gt;
&lt;div class="input"&gt;
&lt;div class="prompt input_prompt"&gt;In [40]:&lt;/div&gt;
&lt;div class="inner_cell"&gt;
    &lt;div class="input_area"&gt;
&lt;div class=" highlight hl-ipython3"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# F, 2 octaves above Middle C&lt;/span&gt;
&lt;span class="n"&gt;chroma&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="mi"&gt;5&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="c1"&gt;# ValueError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div class="output_wrapper"&gt;
&lt;div class="output"&gt;


&lt;div class="output_area"&gt;

    &lt;div class="prompt"&gt;&lt;/div&gt;


&lt;div class="output_subarea output_text output_error"&gt;
&lt;pre&gt;
&lt;span class="ansi-red-fg"&gt;---------------------------------------------------------------------------&lt;/span&gt;
&lt;span class="ansi-red-fg"&gt;ValueError&lt;/span&gt;                                Traceback (most recent call last)
&lt;span class="ansi-green-fg"&gt;&amp;lt;ipython-input-40-3c4313b92e77&amp;gt;&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;()&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;----&amp;gt; 1&lt;/span&gt;&lt;span class="ansi-red-fg"&gt; &lt;/span&gt;chroma&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;3&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;5&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;3&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt; &lt;span class="ansi-red-fg"&gt;# F, 2 octaves above Middle C&lt;/span&gt;

&lt;span class="ansi-green-fg"&gt;/Users/adamwood/amwenv/lib/python3.5/functools.py&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;wrapper&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(*args, **kw)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    741&lt;/span&gt; 
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    742&lt;/span&gt;     &lt;span class="ansi-green-fg"&gt;def&lt;/span&gt; wrapper&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;*&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;**&lt;/span&gt;kw&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;:&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;--&amp;gt; 743&lt;/span&gt;&lt;span class="ansi-red-fg"&gt;         &lt;/span&gt;&lt;span class="ansi-green-fg"&gt;return&lt;/span&gt; dispatch&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;&lt;span class="ansi-cyan-fg"&gt;0&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;__class__&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;*&lt;/span&gt;args&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;**&lt;/span&gt;kw&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    744&lt;/span&gt; 
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;    745&lt;/span&gt;     registry&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;object&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; func

&lt;span class="ansi-green-fg"&gt;&amp;lt;ipython-input-28-f2a6c2b4cc47&amp;gt;&lt;/span&gt; in &lt;span class="ansi-cyan-fg"&gt;_&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;(xy)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     12&lt;/span&gt; &lt;span class="ansi-blue-fg"&gt;@&lt;/span&gt;chroma&lt;span class="ansi-blue-fg"&gt;.&lt;/span&gt;register&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;tuple&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     13&lt;/span&gt; &lt;span class="ansi-green-fg"&gt;def&lt;/span&gt; _&lt;span class="ansi-blue-fg"&gt;(&lt;/span&gt;xy&lt;span class="ansi-blue-fg"&gt;)&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;:&lt;/span&gt;
&lt;span class="ansi-green-fg"&gt;---&amp;gt; 14&lt;/span&gt;&lt;span class="ansi-red-fg"&gt;     &lt;/span&gt;x&lt;span class="ansi-blue-fg"&gt;,&lt;/span&gt;y &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; xy
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     15&lt;/span&gt;     name &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; name_map&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;x&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;
&lt;span class="ansi-green-intense-fg ansi-bold"&gt;     16&lt;/span&gt;     modifier &lt;span class="ansi-blue-fg"&gt;=&lt;/span&gt; modifiers&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;y &lt;span class="ansi-blue-fg"&gt;-&lt;/span&gt; hs_map&lt;span class="ansi-blue-fg"&gt;[&lt;/span&gt;x&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;&lt;span class="ansi-blue-fg"&gt;]&lt;/span&gt;

&lt;span class="ansi-red-fg"&gt;ValueError&lt;/span&gt;: too many values to unpack (expected 2)&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;The translation functions and associated dictionaries need to be extended to include multiple representations such as Unicode, ASCII, and &lt;a href="http://lilypond.org/text-input.html"&gt;Lilypond&lt;/a&gt;.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;Finally, I might also do some experiementation with a hybrid approach that would keep the OO API intact. However, that might get too complicated.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;h2 id="Is-Functional-Really-Better?"&gt;Is Functional Really Better?&lt;a class="anchor-link" href="http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/#Is-Functional-Really-Better?"&gt;¶&lt;/a&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="cell border-box-sizing text_cell rendered"&gt;&lt;div class="prompt input_prompt"&gt;
&lt;/div&gt;&lt;div class="inner_cell"&gt;
&lt;div class="text_cell_render border-box-sizing rendered_html"&gt;
&lt;p&gt;I don't know.&lt;/p&gt;
&lt;p&gt;The difference in execution speed can probably be resolved by cleaning up the OO implememenation and importing the tuple-based arithmetic instead of the union-of-sets approach. I suspect that this would also make the OO code easier to read and reason about.&lt;/p&gt;
&lt;p&gt;I'm not at all convinced that there is &lt;a href="http://harmful.cat-v.org/software/OO_programming/"&gt;something inherently wrong with object oriented code&lt;/a&gt;. However, I do think that a classical paradigm promotes a "different things are different things" mentality. In some cases this is probably helpful. In music, at least in this case, it obscured a fundamental sameness between two important concepts. Functional programming forced me to recognize that sameness. Someone else might have recognized it anyway, and written a good OO implementation first.&lt;/p&gt;
&lt;p&gt;If there is a generalizable lesson here, I think it might this: Think through a few different approaches and paradigms. Try coding up more than one logical implementation in the domain. See how that sheds light on the underlying problems and data structure.&lt;/p&gt;
&lt;p&gt;Also, don't be afraid to redesign things. Especially if you don't have any users yet.&lt;/p&gt;

&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
</description><guid>http://hackwrite.com/posts/object-oriented-vs-functional-modelling-of-musical-arithmetic-in-python/</guid><pubDate>Fri, 10 Nov 2017 19:07:06 GMT</pubDate></item><item><title>Registering Functions Against Object Methods in Python</title><link>http://hackwrite.com/posts/registering-functions-against-object-methods-in-python/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;My big side project right now is a &lt;a href="https://github.com/OphisMusic/ophis"&gt;music theory library in Python, called Ophis&lt;/a&gt;. Among many other concerns, I'm trying to make the API as natural and easy to use as possible. This often means finding ways of creating objects other than &lt;code&gt;ClassName(args)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ophis has the classes &lt;code&gt;Chroma&lt;/code&gt; and &lt;code&gt;Pitch&lt;/code&gt;. A chroma is a note name without an octave (the &lt;em&gt;idea&lt;/em&gt; of C), while a pitch is a chroma with a specified octave (middle C).&lt;/p&gt;
&lt;p&gt;The problem with this is that the conventional way of referring to a pitch would then be:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&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;/pre&gt;


&lt;p&gt;You can see, Ophis has already initialized all the note names (chromae) you would need. We &lt;em&gt;could&lt;/em&gt; do that with pitches...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;C0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pitch&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;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;C1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pitch&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;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# later, in user code...&lt;/span&gt;

&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C1&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;...but I think we all know the problem with that. It requires initializing several hundred pitch objects that may never be used. Most songs don't use every note. And every physical note has multiple names because of enharmonic spelling (F♯ == G♭).&lt;/p&gt;
&lt;p&gt;So, what if the API looked like this?&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&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;/pre&gt;


&lt;p&gt;That's cool. Pretty easy to do, too.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chroma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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;octave&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Pitch&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;octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;h2&gt;What if we went deeper?&lt;/h2&gt;
&lt;p&gt;Once you realize this is a good idea, the next thing you realize is.... what about chords?&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Major&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;Well, that looks pretty similar, doesn't it?&lt;/p&gt;
&lt;p&gt;So, um... okay...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chroma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Pitch&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="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Chord&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;/pre&gt;


&lt;p&gt;There are problems with this.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Definitions for Pitch and Chord are in modules that get loaded after Chroma. This doesn't create any errors (because the function isn't run on load), but still feels wrong.&lt;/li&gt;
&lt;li&gt;It is brittle. If I change the name of &lt;code&gt;Pitch&lt;/code&gt; or &lt;code&gt;Chord&lt;/code&gt;, I have to go back and change it here. The tightly-wound nature of music terminology means I have long-since given up the idea of &lt;em&gt;loose coupling&lt;/em&gt;, but I'm trying to make these types of dependencies only go up the conceptual ladder, not back down it.&lt;/li&gt;
&lt;li&gt;What if I want to add more things to this method? Eventually I'm going to end up creating a series of type checks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I was working through this, I didn't see any way around a series of type checks, but I thought I could solve the first two problems with some creative coding.&lt;/p&gt;
&lt;p&gt;I decided I could register functions into a &lt;code&gt;dict&lt;/code&gt;, stored on the class. The keys for the dict would be types, and the values would be the functions to run when &lt;code&gt;__call__&lt;/code&gt; is called with that particular type as an argument. These functions could be registered at the point when the type that the function is supposed to return is created.&lt;/p&gt;
&lt;p&gt;Something like...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chroma&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;

    &lt;span class="n"&gt;_callable_funcs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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;x_type&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="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;callable&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_callable_funcs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&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;__class__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_callable_funcs&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;x&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="c1"&gt;# This code has not been tested.&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;I got (a version of) this to work, and I was feeling pretty darn proud of myself for thinking of this solution, and implementing it.&lt;/p&gt;
&lt;p&gt;Then I had this feeling like this was all very familiar. Maybe I had read about this type of thing?&lt;/p&gt;
&lt;p&gt;I quickly discovered three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I had read about precisely this in &lt;a href="http://amzn.to/2ruo2td"&gt;&lt;em&gt;Fluent Python&lt;/em&gt; by Luciano Ramalho&lt;/a&gt;. (This is a great book for intermediate Python programmers.)&lt;/li&gt;
&lt;li&gt;This pattern is called &lt;em&gt;single dispatch&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;It is already &lt;a href="https://docs.python.org/3/library/functools.html#functools.singledispatch"&gt;implemented in the standard library&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, I have two problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@singledispatch&lt;/code&gt; decorator only looks at the first argument of a function call. The first argument of a method call is always &lt;code&gt;self&lt;/code&gt;. So, out of the box, this dosn't work for instance methods.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@singledispatch&lt;/code&gt; was &lt;a href="https://www.python.org/dev/peps/pep-0443/"&gt;added in v3.4&lt;/a&gt;, making it still a little newish. Since I'm writing a utility library for others to use, and not my own application, it seems unwise to rely on something that everyone might not have.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But, now I can do two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;See if anyone has already figured out a way to apply &lt;code&gt;@singledispatch&lt;/code&gt; to a method. (&lt;a href="https://stackoverflow.com/questions/24601722/how-can-i-use-functools-singledispatch-with-instance-methods"&gt;Someone has&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;Potentially re-implement &lt;code&gt;@singledispatch&lt;/code&gt; myself, for backwards compatibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Right...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# oph_utils.py&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;singledispatch&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# A re-implementation of @singledispatch&lt;/span&gt;
    &lt;span class="c1"&gt;# has been left as an exercise for the reader&lt;/span&gt;
    &lt;span class="c1"&gt;# because I haven't done one yet.&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;method_dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;span class="sd"&gt;    An extension of functools.singledispatch,&lt;/span&gt;
&lt;span class="sd"&gt;    which looks at the argument after self.&lt;/span&gt;
&lt;span class="sd"&gt;    """&lt;/span&gt;
    &lt;span class="n"&gt;dispatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;singledispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&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;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="vm"&gt;__class__&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;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;
    &lt;span class="n"&gt;update_wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;

&lt;span class="c1"&gt;# chroma.py&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Chroma&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;


    &lt;span class="nd"&gt;@oph_utils.method_dispatch&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__call__&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="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

&lt;span class="c1"&gt;# pitch.py&lt;/span&gt;


&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;chroma&lt;/span&gt; &lt;span class="kn"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;ch&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&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;chroma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;octave&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&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;chroma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chroma&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;octave&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__call__&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# In user code:&lt;/span&gt;

&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&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="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&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="c1"&gt;# True&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;And finally, to encourage this usage...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;


    &lt;span class="fm"&gt;__repr__&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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&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="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chroma&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&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;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;octave&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__repr__&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;span class="c1"&gt;# At a terminal...&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ophis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&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;C&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;/pre&gt;


&lt;p&gt;Feels Pythonic, yes?&lt;/p&gt;
&lt;h2&gt;Further Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://amzn.to/2rLvDTO"&gt;Fluent Python, by Luciano Ramalho&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.python.org/dev/peps/pep-0443/"&gt;PEP 443 -- Single-dispatch generic functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://julien.danjou.info/blog/2013/python-3.4-single-dispatch-generic-function"&gt;Python 3.4 single dispatch, a step into generic functions&lt;/a&gt; (Also a music-related example.)&lt;/li&gt;
&lt;/ul&gt;</description><guid>http://hackwrite.com/posts/registering-functions-against-object-methods-in-python/</guid><pubDate>Thu, 25 May 2017 20:07:32 GMT</pubDate></item><item><title>Intersection of Non-Empty Sets in Python</title><link>http://hackwrite.com/posts/intersection-of-non-empty-sets-in-python/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;Suppose you generate several sets on the fly, and you want to find the elements that are in all the sets. That's easy, it's the intersection of sets.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# One syntax option&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;set_one&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;set_two&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;set_three&lt;/span&gt;

&lt;span class="c1"&gt;# Another option&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_three&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;But let's suppose that one or more of your sets is empty. The intersection of any set and an empty set is an empty set. But, that's not what you want. (Well, it wasn't what I wanted, anyway.)&lt;/p&gt;
&lt;p&gt;Suppose you want the intersection of all non-empty sets.&lt;/p&gt;
&lt;h2&gt;List comprehension&lt;/h2&gt;
&lt;p&gt;If the sets are in a list, you can remove the empties. Then unpack the list into the &lt;code&gt;set.intersection()&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;list_of_sets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;set_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set_three&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Empty sets evaluate to false,&lt;/span&gt;
&lt;span class="c1"&gt;# so will be excluded from list comp.&lt;/span&gt;
&lt;span class="n"&gt;non_empties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;list_of_sets&lt;/span&gt; &lt;span class="k"&gt;if&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;solution_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;non_empties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;The asterisk before &lt;code&gt;non_empties&lt;/code&gt; unpacks the list into a series of positional arguments. This is needed because &lt;code&gt;set.intersection()&lt;/code&gt; takes an arbitrary number of sets, not an iterable full of sets. (It's the same asterisk as in &lt;code&gt;*args&lt;/code&gt; in function definitions.)&lt;/p&gt;
&lt;p&gt;(Note: You &lt;em&gt;could&lt;/em&gt; use a filter instead of a list comprehension, but &lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=98196"&gt;Guido thinks a list comprehension is better&lt;/a&gt;. I agree.)&lt;/p&gt;
&lt;h3&gt;With iterable unpacking (tuple unpacking)&lt;/h3&gt;
&lt;p&gt;In my case, I was generating the sets in my code, and the solution set always contained only one item. And I wanted the item, not a set with the item. So...&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# initialize an empty list&lt;/span&gt;
&lt;span class="n"&gt;list_of_sets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="c1"&gt;# each time I create a set,&lt;/span&gt;
&lt;span class="c1"&gt;# append set to list when it is created,&lt;/span&gt;
&lt;span class="c1"&gt;# instead of naming them individually&lt;/span&gt;
&lt;span class="n"&gt;list_of_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;thing_that_generates_a_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# drop the empties, find the intersection&lt;/span&gt;
&lt;span class="c1"&gt;# and unpack the remaining single element&lt;/span&gt;
&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersection&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;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;list_of_sets&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;The comma after &lt;code&gt;solution&lt;/code&gt; turns the assignment into a tuple unpacking. If you unpack a collection of one, you get the single item.&lt;/p&gt;
&lt;p&gt;By the way, if you end up with more than one item in your collection, and only want the first item, you can do:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;first_item, *_ = some_collection
&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; indicates a variable number of positional arguments (it's the same asterisk as in &lt;code&gt;*args&lt;/code&gt; and in passing the list to &lt;code&gt;set.intersection()&lt;/code&gt; above), and the underscore is used as a convention for "not using this stuff."&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;# you could have done this instead

first_item, *stuff_i_will_not_care_about = some_collection
&lt;/pre&gt;


&lt;p&gt;I'll be using that &lt;code&gt;*_&lt;/code&gt; below, in the actual code.&lt;/p&gt;
&lt;h2&gt;Why would you ever do this?&lt;/h2&gt;
&lt;h3&gt;The generalized problem&lt;/h3&gt;
&lt;p&gt;From a pool of items, there are three attributes to select for. Specifying any two of them should produce one and only one result.&lt;/p&gt;
&lt;h3&gt;More specifically...&lt;/h3&gt;
&lt;p&gt;Musical intervals.&lt;/p&gt;
&lt;p&gt;A musical interval has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a quality (Major, Minor, Perfect, Augment, or Diminished)&lt;/li&gt;
&lt;li&gt;a number (Unison (1), Second (2), Third (3) ... Octave (8))&lt;/li&gt;
&lt;li&gt;a distance of half_steps (for example, a major third is 4 half steps)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you know any two of these, you can select the correct one.&lt;/p&gt;
&lt;h2&gt;Some actual code&lt;/h2&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Interval&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;

  &lt;span class="c1"&gt;#####################################&lt;/span&gt;
  &lt;span class="c1"&gt;# ... all sorts of things removed ...&lt;/span&gt;
  &lt;span class="c1"&gt;#####################################&lt;/span&gt;


  &lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;# all instances of Interval&lt;/span&gt;


  &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_intervals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&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;quality&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;number&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;half_steps&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="sd"&gt;"""Return a set of intervals."""&lt;/span&gt;

      &lt;span class="n"&gt;candidate_sets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

      &lt;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;quality&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;half_steps&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;half_steps&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="n"&gt;candidate_sets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidate_sets&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&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="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_interval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quality&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;number&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;half_steps&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="sd"&gt;""" Return a single interval."""&lt;/span&gt;

      &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_intervals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;half_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;half_steps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="c1"&gt;## if there was not one and only one result&lt;/span&gt;
      &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

          &lt;span class="c1"&gt;# only select by half_steps&lt;/span&gt;
          &lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;half_steps&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;half_steps&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

          &lt;span class="c1"&gt;# select the first one,&lt;/span&gt;
          &lt;span class="c1"&gt;# based on quality priority:&lt;/span&gt;
          &lt;span class="c1"&gt;# Perfect, Major, Minor, Dim, Aug&lt;/span&gt;
          &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&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;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;In &lt;a href="https://github.com/OphisMusic/ophis"&gt;the actual code&lt;/a&gt;, there's a bunch of other things going on, but this is the general idea.&lt;/p&gt;
&lt;h2&gt;Another approach&lt;/h2&gt;
&lt;p&gt;For my specific use case, another approach is simply to not create a set for the unspecified attribute.&lt;/p&gt;
&lt;pre class="code literal-block"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;quality&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;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;quality&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&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;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;number&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;half_steps&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;span class="n"&gt;candidate_sets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instances&lt;/span&gt; &lt;span class="k"&gt;if&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;half_steps&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;half_steps&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;In my working code, I actually do both. This allows for a potentially meaningful result even if something is specified incorrectly. I could have decided to let bad input cause explicit failure, but I &lt;strong&gt;think&lt;/strong&gt; I'd rather not in this case.&lt;/p&gt;
&lt;h2&gt;So... what's the point?&lt;/h2&gt;
&lt;p&gt;This post looks like a tutorial on list comprehension. Or maybe set operations. But really this post is about problem solving while writing code.&lt;/p&gt;
&lt;p&gt;The code solution to this problem is really easy... but &lt;em&gt;only if you've figured out the problem you need to solve&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I started with the following problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Find the intersection of all non-empty sets, from an arbitrary pool of sets, not knowing which ones would be empty.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I started Googling variations on that theme. But there aren't any "intersection of just the good sets" functions. Then I tried to start writing a question for Stack Overflow, and as soon as I had written the title, I knew the answer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting with a collection of sets, drop the empty sets and find the intersection of the remaining sets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As soon as I broke my one problem into two steps, the problem was immediately solved:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new collection without the empties. (List comp.)&lt;/li&gt;
&lt;li&gt;Find the intersection of &lt;em&gt;that&lt;/em&gt; list.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At the same moment I realized these steps, it also become clear that the original group of sets should be a collection, not just several unrelated objects.&lt;/p&gt;
&lt;p&gt;So, the moral of the story is...&lt;/p&gt;
&lt;p&gt;If you can't find the solution to your specific problem, restate your problem as a series of steps.&lt;/p&gt;</description><guid>http://hackwrite.com/posts/intersection-of-non-empty-sets-in-python/</guid><pubDate>Sat, 15 Apr 2017 14:48:40 GMT</pubDate></item><item><title>Verbiage</title><link>http://hackwrite.com/posts/verbiage/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;I hate the word &lt;em&gt;verbiage&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;First, we need to deal with the fact that it is the &lt;em&gt;wrong word&lt;/em&gt;. Most of the time, when people say &lt;em&gt;verbiage&lt;/em&gt;, they really mean &lt;em&gt;verbage&lt;/em&gt; --- that is, the wording. &lt;em&gt;Verbiage&lt;/em&gt;, properly, means excessive wordiness, not the specifics of word choice.&lt;/p&gt;
&lt;p&gt;But this isn't what I hate about it. I would hate it just as much if it meant precisely what every one uses it to mean. My problem is with the idea itself. I hate what people are saying when they say &lt;em&gt;verbiage&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Every time I have ever heard the word &lt;em&gt;verbiage&lt;/em&gt;, the person has been talking about &lt;em&gt;the precise way that something is worded&lt;/em&gt;. The context is always about improving something.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Can you make this more clear by fixing up the verbiage?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;After you get the first draft of the design done, ask Adam to help you clean up the verbiage.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Maybe we can change the verbiage on this form to make it more user friendly.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without fail, a request to &lt;em&gt;work on the verbiage&lt;/em&gt; is symptomatic of a deeply flawed design and engineering process. We got to this point because people were &lt;a href="http://hackwrite.com/posts/designing-vs-decorating/"&gt;decorating, not designing&lt;/a&gt;, and now we are going to try to get out of it by changing the words the user sees.&lt;/p&gt;
&lt;p&gt;This causes more problems, of course.&lt;/p&gt;
&lt;p&gt;The reason the words aren't clear and precise in the UI is that the mental model developed by the engineering team is either confused or just plain wrong. In order to make the application easy to use, our Verbiage Specialist has to overlay a new mental model --- often, the one that should have been used in the first place. This new mental model, and the collection of verbages that go with it, will be imprecise and incomplete because the Verbiage Engineering Team can't tell the developers to restructure the database and rename all the application's variables. The result is that the UI becomes temporarily easier to use, but at the cost of taking on additional Verbiage Debt. Somewhere deep in an internal wiki or Confluence page is a OVM (Object-Verbiage Mapper) glossary telling you that &lt;code&gt;dev:event_property =&amp;gt; user:"Device Status"&lt;/code&gt;. But nobody reads internal wiki pages, so the problem just gets worse.&lt;/p&gt;
&lt;p&gt;You cannot fix an application by redecorating the UI. Fixing the &lt;em&gt;verbiage&lt;/em&gt; is just redecorating + technical debt. If you find yourself &lt;em&gt;fixing up the verbiage&lt;/em&gt;, the problems are much deeper.&lt;/p&gt;
&lt;p&gt;So how do you avoid Verbiage Debt?&lt;/p&gt;
&lt;p&gt;Stop treating writers as Verbiage Technicians and think of them as Verbiage Architects. (I'm sure there's a &lt;a href="http://www.uxbooth.com/articles/what-is-ux-writing/"&gt;good word for this&lt;/a&gt; already.) Your Verbiage Team, along with your Pictures of Things Engineers, need to be involved &lt;strong&gt;from the beginning&lt;/strong&gt; with the design of your application, and they need to be fully-fledged members of the engineering team --- not hired hands, consultants, helpers, or otherwise after-the-facters.&lt;/p&gt;
&lt;p&gt;Building software has more to do with creating mental models than it does with writing code. Humans create mental models in language and pictures.&lt;/p&gt;
&lt;p&gt;Your language and pictures people are as important as your coders.&lt;/p&gt;</description><guid>http://hackwrite.com/posts/verbiage/</guid><pubDate>Wed, 08 Feb 2017 16:26:57 GMT</pubDate></item><item><title>Designing vs. Decorating</title><link>http://hackwrite.com/posts/designing-vs-decorating/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;My wife spent some time in an Interior Design master's degree program. One of the things that frequently frustrated her was the conflation, by people outside the industry, of interior design and interior decorating.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;"Oh, so like, you're learning how to pick out furniture and stuff."&lt;/li&gt;
&lt;li&gt;"Can you help me pick paint colors in my bedroom?"&lt;/li&gt;
&lt;li&gt;"That's cool, like that show on HGTV."&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Decorating is primarily about aesthetics --- how things look. Design is about function --- how things work. There is certainly overlap between the professions, but their focus and concern is very different.&lt;/p&gt;
&lt;p&gt;At least, though, nearly everyone in the industry --- and certainly everyone at her school --- understood the difference. Since my wife was there the school has actually &lt;a href="http://the-bac.edu/academics/school-of-interior-architecture"&gt;changed the name of the program to &lt;em&gt;Interior Architecture&lt;/em&gt;&lt;/a&gt;, to make the focus more clear.&lt;/p&gt;
&lt;p&gt;I'm not sure the software industry as a whole understands the difference between decorating and design. Part of the problem is that we don't use the word "decorator," to describe people with graphics skills and no sense of the underlying software. Everyone is a "designer." The best we have done is to try to make distinctions between "UX Design" and "Graphic Design."&lt;/p&gt;
&lt;p&gt;In fact, I think the push in the last decade or so to use the word "UX" is an attempt to make the distinction. Unfortunately, I don't think it has helped. Like Tech Writers calling themselves "Documentation Specialists," the change in label has been driven as much by a desire for a cooler resume as by any real change in practices. The distinction we need to make is not between "graphics" and "UX," and certainly not between "UX" and "UI" (as if those are, you know, &lt;em&gt;actually&lt;/em&gt; different things, &lt;em&gt;really&lt;/em&gt;). The distinction we need to make is between design and decoration.&lt;/p&gt;
&lt;p&gt;Have you ever sat in a redesign review that solved exactly none of the problems of the original design? The new thing looks better, but it functions the same. &lt;strong&gt;Decorating&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Have you ever been involved in a process where some non-engineer Product Manager drew pictures of screens and buttons, and then someone with Photoshop skills and no coding experience turned that into a mockup? &lt;strong&gt;Decorating.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Have you ever been asked, after the graphics person has completed an entire set of screen mockups, to "help with some of the verbiage" in order to make things more clear? &lt;strong&gt;Decorating.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Any process that separates out the work of contributors --- first the engineers do something and then hand it off to the graphics person and then the tech writer writes about it later --- will tend toward decorating. Design requires people to actually talk to each other, preferably in the same room. Design requires that a person drawing and labelling a form input understand the conceptual model the form is interacting with.&lt;/p&gt;
&lt;p&gt;I suggest we stop futzing with labels for types of people and buzzwords that feel helpful but aren't. This problem cannot be solved by finding an even cooler replacement word for "UX," and then blogging about how "UX is dead, we're doing XZ now." Just keep "design" and "decoration" in your head as an evaluative tool. Look at how things are being done and ask yourself --- it this designing or is it decorating? Then, if there's too much decorating, don't spend a lot of energy convincing people about the difference. Just begin to change the process.&lt;/p&gt;
&lt;p&gt;And don't let someone with Photoshop skills redesign an app they don't understand and have never used.&lt;/p&gt;</description><guid>http://hackwrite.com/posts/designing-vs-decorating/</guid><pubDate>Mon, 06 Feb 2017 13:41:06 GMT</pubDate></item><item><title>Docs as Code</title><link>http://hackwrite.com/posts/docs-as-code/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;h2&gt;Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Docs are written in plain text formats such as Markdown or reStructured Text.&lt;/li&gt;
&lt;li&gt;Docs are stored as flat files, not database entries.&lt;/li&gt;
&lt;li&gt;Docs are authored in a code editor of the writer's choice, not a monolithic authoring application.&lt;/li&gt;
&lt;li&gt;Docs are kept under version control.&lt;/li&gt;
&lt;li&gt;Doc versions are organized in parallel to product versions.&lt;/li&gt;
&lt;li&gt;Docs are built and deployed from source in an automated process that mirrors product deployment.&lt;/li&gt;
&lt;li&gt;Docs are automatically tested for internal consistency and compliance to style guides.&lt;/li&gt;
&lt;li&gt;Whenever reasonable, writers use the same tools and processes as developers.&lt;/li&gt;
&lt;li&gt;Writers are integrated into the development team.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Writers have more control over their authoring environment.&lt;/li&gt;
&lt;li&gt;Less friction in the authoring process.&lt;/li&gt;
&lt;li&gt;Elimination of inconsistencies between docs and product.&lt;/li&gt;
&lt;li&gt;Less need for human proofreading.&lt;/li&gt;
&lt;li&gt;Coordinated releases of docs with product.&lt;/li&gt;
&lt;li&gt;Developers are more likely to contribute to docs.&lt;/li&gt;
&lt;li&gt;Writers and developers have more awareness of and respect for each others' work.&lt;/li&gt;
&lt;li&gt;Authoring and deployment tools are mostly free; hosting requires less overhead.&lt;/li&gt;
&lt;/ul&gt;</description><guid>http://hackwrite.com/posts/docs-as-code/</guid><pubDate>Wed, 01 Feb 2017 13:33:02 GMT</pubDate></item><item><title>DocOps Isn't Just the Fun Part</title><link>http://hackwrite.com/posts/docops-not-just-fun-part/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;Somewhere in the last year I decided I was into &lt;em&gt;DocOps&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What that really meant for me is that I am into &lt;em&gt;Docs-as-code&lt;/em&gt;, which is a related trend, but not quite the same. I care about  things like single-source documents (DRY), version control, plain text editing, style linting, and automated deployment. I write little Python or Bash scripts to pipe tools together and customize the output of static site generators. I'm learning a lot, having a lot of fun, and finally weaving together a number of different skill sets and interests I've picked up over the years (writing, coding, project management).&lt;/p&gt;
&lt;p&gt;When I was the only writer at a startup, this was all really effective. I could fool myself into thinking I was &lt;em&gt;doing DocOps&lt;/em&gt;. And maybe I was, but only in that particular context.&lt;/p&gt;
&lt;p&gt;But now I work at a big, hulking enterprise company. And all of the sudden it is clear that DocOps isn't just the fun technology bits, just like how DevOps isn't just about knowing how to deploy Docker on Kubernetes. It's about dealing with people and dealing with organizations.&lt;/p&gt;
&lt;p&gt;I just want to stand up my docs somewhere. "Give me SSH access to a directory with a public URL." At the startup I just made a decision and had live docs published my second or third day there. At the enterprise? Not so simple. My tooling has to go through security checks. Engineers have to sign off on deployment processes. Customer service has a vested interest in how documents are delivered. Can we integrate to Salesforce knowledge base? How do I &lt;code&gt;pip install&lt;/code&gt; from behind a firewall?&lt;/p&gt;
&lt;p&gt;If I'm &lt;em&gt;into DocOps&lt;/em&gt;, this is what I'm into. Not just hacking on writing tools (as much fun as that is), but also being effective in an organization. I was very effective in a startup, where hacking on things was how the organization operated. Now I have to level up and learn how to be effective at scale.&lt;/p&gt;</description><guid>http://hackwrite.com/posts/docops-not-just-fun-part/</guid><pubDate>Thu, 26 Jan 2017 14:56:30 GMT</pubDate></item><item><title>The Real Reason I Love Static Site Generators</title><link>http://hackwrite.com/posts/real-reason-i-love-static-site-generators/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;p&gt;There's a lot to like about &lt;a href="https://www.staticgen.com/"&gt;static site generators&lt;/a&gt; like &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, &lt;a href="https://getnikola.com/"&gt;Nikola&lt;/a&gt;, and &lt;a href="http://www.sphinx-doc.org/"&gt;Sphinx&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hosting is much simpler, and can usually be done for free.&lt;/li&gt;
&lt;li&gt;Static sites are inherently more secure than dynamic ones.&lt;/li&gt;
&lt;li&gt;Very fast page load times.&lt;/li&gt;
&lt;li&gt;Authoring in a code editor that I have control over.&lt;/li&gt;
&lt;li&gt;Markdown and reStructured Text are both faster to type than HTML or rich content in a WYSIWYG editor.&lt;/li&gt;
&lt;li&gt;Version control.&lt;/li&gt;
&lt;li&gt;The ability to manage the build and deploy process like code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are probably more benefits I'm not thinking of at the moment. When I first started using Jekyll, my main motivation was wanting to simplify hosting and exert control over authoring. I discovered the other benefits along the way, and they have really changed my professional life.&lt;/p&gt;
&lt;p&gt;But I've realized there's one thing that has come to matter the most to me:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Static sites revive and make real the notion of a document on the web.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In database-backed CMSes, the pretty URL is a noble lie. Content is smeared around in a database and accessed through &lt;code&gt;?id=1234&lt;/code&gt; parameters or internal query mechanisms. This is fine, and really the only way to handle massive amounts of content.&lt;/p&gt;
&lt;p&gt;But the web was built to serve documents, not database results. In an age where content-as-data is on such hyperdrive that people think a &lt;a href="http://sennajs.com/examples/blog/"&gt;single-page app blog system&lt;/a&gt; is a reasonable idea, it is calming to use a technology that works the way the web was always supposed to work.&lt;/p&gt;
&lt;p&gt;And this has as much to do with the mental model as with the technology. (Maybe more.) The individual documents that make up a static site are handled &lt;em&gt;as documents&lt;/em&gt; before being processed to HTML. If I want to change the content on some blog post, I edit a file on my local computer. I don't have to log in and use an application. It is transparent, and there's a direct relationship between a single file in my source and a single URI on my site. Now it feels like the URI actually identifies a &lt;em&gt;resource&lt;/em&gt;, and is not just a cleverly-disguised search pattern.&lt;/p&gt;
&lt;p&gt;I understand why we moved past the web of documents. But if you're producing documents, maybe it's the right model.&lt;/p&gt;</description><guid>http://hackwrite.com/posts/real-reason-i-love-static-site-generators/</guid><pubDate>Wed, 25 Jan 2017 14:07:37 GMT</pubDate></item><item><title>File Names</title><link>http://hackwrite.com/posts/file-names/</link><dc:creator>Adam Michael Wood</dc:creator><description>&lt;div&gt;&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things.&lt;br&gt;
-- Phil Karlton&lt;/p&gt;
&lt;p&gt;I cannot help you with cache invalidation.&lt;br&gt;
-- Adam Michael Wood&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently saw a question about file names in the &lt;a href="https://www.facebook.com/groups/episcopalcommunicators/"&gt;Episcopal Communicators Facebook Group&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Question about file names." src="http://hackwrite.com/img/filename-question.png"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a question about filenames for websites.&lt;/p&gt;
&lt;p&gt;When we first developed our website, our consultant told me that when we put a file on there, it's important to give the file a date and a unique and descriptive name.&lt;/p&gt;
&lt;p&gt;While that works for some files, it doesn't for others. It caused me to end up with a lot of old files on my website.&lt;/p&gt;
&lt;p&gt;What I changed was that I stopped changing file names. So instead of mileage_rates_2016.pdf, I just call it mileage_rates.pdf. That way every link is correct, everywhere on the site.&lt;/p&gt;
&lt;p&gt;However, when we link to outside websites, like the wider church's site, we end up with obsolete links. Case in point: the Manual of Business Methods:&lt;/p&gt;
&lt;p&gt;We had full_manual_updated_09-30-2013.pdf.&lt;/p&gt;
&lt;p&gt;And now the link is full_manual_updated_012815_0.pdf&lt;/p&gt;
&lt;p&gt;Is there any need to give dates to files like this? It's important for the organization to archive old versions, but is there any need to have unique names so that websites like ours end up with older versions?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I summed a few file name best practices, but... I have &lt;em&gt;a lot&lt;/em&gt; to say about this topic. File naming is one of those weird little things I have irrationally strong feelings about, and the ubiquity of bad file naming practices is a constant source of rage in my life.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://hackwrite.com/posts/file-names/"&gt;Read more…&lt;/a&gt; (7 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><guid>http://hackwrite.com/posts/file-names/</guid><pubDate>Fri, 20 Jan 2017 14:31:12 GMT</pubDate></item></channel></rss>