<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Real Python</title>
  <link href="https://realpython.com/atom.xml" rel="self"/>
  <link href="https://realpython.com/"/>
  <updated>2019-03-28T14:00:00+00:00</updated>
  <id>https://realpython.com/</id>
  <author>
    <name>Real Python</name>
  </author>

  
    <entry>
      <title>Working With JSON Data in Python</title>
      <id>https://realpython.com/courses/working-json-data-python/</id>
      <link href="https://realpython.com/courses/working-json-data-python/"/>
      <updated>2019-03-28T14:00:00+00:00</updated>
      <summary>Learn how to work with Python&#39;s built-in &quot;json&quot; module to serialize the data in your programs into JSON format. Then, you&#39;ll deserialize some JSON from an online API and convert it into Python objects.</summary>
      <content type="html">
        &lt;p&gt;JSON is a lightweight data-interchange format. It allows us to represent the objects in our Python programs as human-readable text that can be sent over the internet. Lots of APIs and databases use JSON for communication.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll learn how to work with Python&amp;rsquo;s built-in &lt;code&gt;json&lt;/code&gt; module to serialize the data in your programs into JSON format. Then, you&amp;rsquo;ll deserialize some JSON from an online API and convert it into Python objects.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>How to Stand Out in a Python Coding Interview</title>
      <id>https://realpython.com/python-coding-interview-tips/</id>
      <link href="https://realpython.com/python-coding-interview-tips/"/>
      <updated>2019-03-27T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to take your Python coding interview skills to the next level and use Python&#39;s built-in functions and modules to solve problems faster and more easily.</summary>
      <content type="html">
        &lt;p&gt;You&amp;rsquo;ve made it past the phone call with the recruiter, and now it&amp;rsquo;s time to show that you know how to solve problems with actual code. Whether it&amp;rsquo;s a HackerRank exercise, a take-home assignment, or an onsite whiteboard interview, this is your moment to prove your coding interview skills.&lt;/p&gt;
&lt;p&gt;But interviews aren&amp;rsquo;t just about solving problems: they&amp;rsquo;re also about showing that you can write clean production code. This means that you have a deep knowledge of Python&amp;rsquo;s built-in functionality and libraries. This knowledge shows companies that you can move quickly and won&amp;rsquo;t duplicate functionality that comes with the language just because you don&amp;rsquo;t know it exists.&lt;/p&gt;
&lt;p&gt;At &lt;em&gt;Real Python&lt;/em&gt;, we&amp;rsquo;ve put our heads together and discussed what tools we&amp;rsquo;re always impressed to see in coding interviews. This article will walk you through the best of that functionality, starting with Python built-ins, then Python&amp;rsquo;s native support for data structures, and finally Python&amp;rsquo;s powerful (and often underappreciated) standard library.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;enumerate()&lt;/code&gt; to iterate over both indices and values&lt;/li&gt;
&lt;li&gt;Debug problematic code with &lt;code&gt;breakpoint()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Format strings effectively with f-strings&lt;/li&gt;
&lt;li&gt;Sort lists with custom arguments&lt;/li&gt;
&lt;li&gt;Use generators instead of list comprehensions to conserve memory&lt;/li&gt;
&lt;li&gt;Define default values when looking up dictionary keys&lt;/li&gt;
&lt;li&gt;Count hashable objects with the &lt;code&gt;collections.Counter&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Use the standard library to get lists of permutations and combinations&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;select-the-right-built-in-function-for-the-job&quot;&gt;Select the Right Built-In Function for the Job&lt;/h2&gt;
&lt;p&gt;Python has a large standard library but only a small library of &lt;a href=&quot;https://docs.python.org/library/functions.html&quot;&gt;built-in functions&lt;/a&gt;, which are always available and don&amp;rsquo;t need to be imported. It&amp;rsquo;s worth going through each one, but until you get the chance to do so, here are a few built-in functions worth understanding how to use, and in the case of some of them, what alternatives to use instead.&lt;/p&gt;
&lt;h3 id=&quot;iterate-with-enumerate-instead-of-range&quot;&gt;Iterate With &lt;code&gt;enumerate()&lt;/code&gt; Instead of &lt;code&gt;range()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This scenario might come up more than any other in coding interviews: you have a list of elements, and you need to iterate over the list with access to both the indices and the values.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a classic coding interview question named FizzBuzz that can be solved by iterating over both indices and values. In FizzBuzz, you are given a list of integers. Your task is to do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Replace all integers that are evenly divisible by &lt;code&gt;3&lt;/code&gt; with &lt;code&gt;&quot;fizz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace all integers divisible by &lt;code&gt;5&lt;/code&gt; with &lt;code&gt;&quot;buzz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace all integers divisible by both &lt;code&gt;3&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt; with &lt;code&gt;&quot;fizzbuzz&quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Often, developers will solve this problem with &lt;code&gt;range()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;fizzbuzz&amp;#39;, 22, 14, &amp;#39;buzz&amp;#39;, 97, &amp;#39;fizz&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Range allows you to access the elements of &lt;code&gt;numbers&lt;/code&gt; by index and is a useful tool &lt;a href=&quot;https://realpython.com/python-range/&quot;&gt;for some situations&lt;/a&gt;. But in this case, where you want to get each element&amp;rsquo;s index and value at the same time, a more elegant solution uses &lt;code&gt;enumerate()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizzbuzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;fizz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;buzz&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;fizzbuzz&amp;#39;, 22, 14, &amp;#39;buzz&amp;#39;, 97, &amp;#39;fizz&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For each element, &lt;code&gt;enumerate()&lt;/code&gt; returns a counter and the element value. The counter defaults to &lt;code&gt;0&lt;/code&gt;, which conveniently is also the element&amp;rsquo;s index. Don&amp;rsquo;t want to start your count at &lt;code&gt;0&lt;/code&gt;? Just use the optional &lt;code&gt;start&lt;/code&gt; parameter to set an offset:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;97&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;52 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;53 22&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;54 14&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;55 65&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;56 97&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;57 72&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By using the &lt;code&gt;start&lt;/code&gt; parameter, we access all of the same elements, starting with the first index, but now our count starts from the specified integer value.&lt;/p&gt;
&lt;h3 id=&quot;use-list-comprehensions-instead-of-map-and-filter&quot;&gt;Use List Comprehensions Instead of &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;I think dropping filter() and map() is pretty uncontroversial[.]&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; &lt;em&gt;Guido van Rossum, Python&amp;rsquo;s creator&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He may have been wrong about it being uncontroversial, but Guido had good reasons for wanting to remove &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt; from Python. One reason is that Python supports list comprehensions, which are often easier to read and support the same functionality as &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first take a look at how we&amp;rsquo;d structure a call to &lt;code&gt;map()&lt;/code&gt; and the equivalent list comprehension:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[16, 4, 1, 36, 81, 49]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;square&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[16, 4, 1, 36, 81, 49]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Both approaches, using &lt;code&gt;map()&lt;/code&gt; and the list comprehension, return the same values, but the list comprehension is easier to read and understand.&lt;/p&gt;
&lt;p&gt;Now we can do the same thing for the &lt;code&gt;filter()&lt;/code&gt; and its equivalent list comprehension:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 9, 7]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;numbers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_odd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 9, 7]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like we saw with &lt;code&gt;map()&lt;/code&gt;, the &lt;code&gt;filter()&lt;/code&gt; and list comprehension approaches return the same value, but the list comprehension is easier to follow.&lt;/p&gt;
&lt;p&gt;Developers who come from other languages may disagree that list comprehensions are easier to read than &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt;, but in my experience beginners are able to pick up list comprehensions much more intuitively.&lt;/p&gt;
&lt;p&gt;Either way, you&amp;rsquo;ll rarely go wrong using list comprehensions in a coding interview, as it will communicate that you know what&amp;rsquo;s most common in Python.&lt;/p&gt;
&lt;h3 id=&quot;debug-with-breakpoint-instead-of-print&quot;&gt;Debug With &lt;code&gt;breakpoint()&lt;/code&gt; Instead of &lt;code&gt;print()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You may have debugged a small problem by adding &lt;code&gt;print()&lt;/code&gt; to your code and seeing what was printed out. This approach works well at first but quickly becomes cumbersome. Plus, in a coding interview setting, you hardly want &lt;code&gt;print()&lt;/code&gt; calls peppered throughout your code.&lt;/p&gt;
&lt;p&gt;Instead, you should be using a debugger. For non-trivial bugs, it&amp;rsquo;s almost always faster than using &lt;code&gt;print()&lt;/code&gt;, and given that debugging is a big part of writing software, it shows that you know how to use tools that will let you develop quickly on the job.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re using Python 3.7, you don&amp;rsquo;t need to import anything and can just call &lt;a href=&quot;https://realpython.com/python37-new-features/#the-breakpoint-built-in&quot;&gt;&lt;code&gt;breakpoint()&lt;/code&gt;&lt;/a&gt; at the location in your code where you&amp;rsquo;d like to drop into the debugger:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Some complicated code with bugs&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;breakpoint()&lt;/code&gt; will put you into &lt;a href=&quot;https://realpython.com/python-debugging-pdb/&quot;&gt;&lt;code&gt;pdb&lt;/code&gt;&lt;/a&gt;, which is the default Python debugger. On Python 3.6 and older, you can do the same by importing &lt;code&gt;pdb&lt;/code&gt; explicitly:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pdb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pdb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_trace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like &lt;code&gt;breakpoint()&lt;/code&gt;, &lt;code&gt;pdb.set_trace()&lt;/code&gt; will put you into the &lt;code&gt;pdb&lt;/code&gt; debugger. It&amp;rsquo;s just not quite as clean and is a tad more to remember.&lt;/p&gt;
&lt;p&gt;There are other debuggers available that you may want to try, but &lt;code&gt;pdb&lt;/code&gt; is part of the standard library, so it&amp;rsquo;s always available. Whatever debugger you prefer, it&amp;rsquo;s worth trying them out to get used to the workflow before you&amp;rsquo;re in a coding interview setting.&lt;/p&gt;
&lt;h3 id=&quot;format-strings-with-f-strings&quot;&gt;Format strings With f-Strings&lt;/h3&gt;
&lt;p&gt;Python has a lot of different ways to handle string formatting, and it can be tricky to know what to use. In fact, we tackle formatting in depth in two separate articles: one about &lt;a href=&quot;https://realpython.com/python-string-formatting/&quot;&gt;string formatting in general&lt;/a&gt; and one specifically &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;focused on f-strings&lt;/a&gt;. In a coding interview, where you&amp;rsquo;re (hopefully) using Python 3.6+, the suggested formatting approach is Python&amp;rsquo;s f-strings.&lt;/p&gt;
&lt;p&gt;f-strings support use of the &lt;a href=&quot;https://docs.python.org/3/library/string.html#format-specification-mini-language&quot;&gt;string formatting mini-language&lt;/a&gt;, as well as powerful string interpolation. These features allow you to add variables or even valid Python expressions and have them evaluated at runtime before being added to the string:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_name_and_decades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;My name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{name}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; and I&amp;#39;m {age / 10:.5f} decades old.&amp;quot;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_name_and_decades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Maria&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;My name is Maria and I&amp;#39;m 3.10000 decades old.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The f-string allows you to put &lt;code&gt;Maria&lt;/code&gt; into the string and add her age with the desired formatting in one succinct operation.&lt;/p&gt;
&lt;p&gt;The one risk to be aware of is that if you&amp;rsquo;re outputting user-generated values, then that can introduce security risks, in which case &lt;a href=&quot;https://realpython.com/python-string-formatting/#4-template-strings-standard-library&quot;&gt;Template Strings&lt;/a&gt; may be a safer option.&lt;/p&gt;
&lt;h3 id=&quot;sort-complex-lists-with-sorted&quot;&gt;Sort Complex Lists With &lt;code&gt;sorted()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Plenty of coding interview questions require some kind of sorting, and there are multiple valid ways you can sort items. Unless the interviewer wants you to implement your own sorting algorithm, it&amp;rsquo;s usually best to use &lt;code&gt;sorted()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve probably seen the most simple uses of sorting, such as sorting lists of numbers or strings in ascending or descending order:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[1, 2, 3, 4, 5, 6, 7]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cat&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;dog&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;cheetah&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rhino&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;bear&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;rhino&amp;#39;, &amp;#39;dog&amp;#39;, &amp;#39;cheetah&amp;#39;, &amp;#39;cat&amp;#39;, &amp;#39;bear]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By default, &lt;code&gt;sorted()&lt;/code&gt; has sorted the input in ascending order, and the &lt;code&gt;reverse&lt;/code&gt; keyword argument causes it to sort in descending order.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth knowing about the optional keyword argument &lt;code&gt;key&lt;/code&gt; that lets you specify a function that will be called on every element prior to sorting. Adding a function allows custom sorting rules, which are especially helpful if you want to sort more complex data types:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;penguin&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Stephanie&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;elephant&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Devon&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;type&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;puma&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Moe&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;elephant&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Devon&amp;#39;, &amp;#39;age&amp;#39;: 3},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;puma&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Moe&amp;#39;, &amp;#39;age&amp;#39;: 5},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    {&amp;#39;type&amp;#39;: &amp;#39;penguin&amp;#39;, &amp;#39;name&amp;#39;: &amp;#39;Stephanie, &amp;#39;age&amp;#39;: 8},&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By passing in a &lt;a href=&quot;https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions&quot;&gt;lambda function&lt;/a&gt; that returns each element&amp;rsquo;s age, you can easily sort a list of dictionaries by a single value of each of those dictionaries. In this case, the dictionary is now sorted in ascending order by age.&lt;/p&gt;
&lt;h2 id=&quot;leverage-data-structures-effectively&quot;&gt;Leverage Data Structures Effectively&lt;/h2&gt;
&lt;p&gt;Algorithms get a lot of attention in coding interviews, but data structures are arguably even more important. In a coding interviewing context, picking the right data structure can have a major impact on performance.&lt;/p&gt;
&lt;p&gt;Beyond theoretical data structures, Python has powerful and convenient functionality built into its standard data structure implementations. These data structures are incredibly useful in coding interviews because they give you lots of functionality by default and let you focus your time on other parts of the problem.&lt;/p&gt;
&lt;h3 id=&quot;store-unique-values-with-sets&quot;&gt;Store Unique Values With Sets&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll often need to remove duplicate elements from an existing dataset. New developers will sometimes do so with lists when they should be using sets, which enforce the uniqueness of all elements.&lt;/p&gt;
&lt;p&gt;Pretend you have a function named &lt;code&gt;get_random_word()&lt;/code&gt;. It will always return a random selection from a small set of words:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;all the words in the world&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;re supposed to call &lt;code&gt;get_random_word()&lt;/code&gt; repeatedly to get 1000 random words and then return a data structure containing every unique word. Here are two common, suboptimal approaches and one good approach.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bad Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get_unique_words()&lt;/code&gt; stores values in a list then converts the list into a set:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach isn&amp;rsquo;t terrible, but it unnecessarily creates a list and then converts it to a set. Interviewers almost always notice (and ask about) this type of design choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worse Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To avoid converting from a list to a set, you now store values in a list without using any other data structures. You then test for uniqueness by comparing new values with all elements currently in the list:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is worse than the first approach, because you have to compare every new word against every word already in the list. That means that as the number of words grows, the number of lookups grows quadratically. In other words, the time complexity grows on the order of O(N²).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Good Approach&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now, you skip using lists altogether and instead use a set from the start:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_random_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_unique_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;world&amp;#39;, &amp;#39;all&amp;#39;, &amp;#39;the&amp;#39;, &amp;#39;words&amp;#39;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This may not look much different than the other approaches except for making use of a set from the beginning. If you consider what&amp;rsquo;s happening within &lt;code&gt;.add()&lt;/code&gt;, it even sounds like the second approach: get the word, check if it&amp;rsquo;s already in the set, and if not, add it to the data structure.&lt;/p&gt;
&lt;p&gt;So why is using a set different from the second approach?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s different because sets store elements in a manner that allows near-constant-time checks whether a value is in the set or not, unlike lists, which require linear-time lookups. The difference in lookup time means that the &lt;a href=&quot;https://stackoverflow.com/a/487278/2141768&quot;&gt;time complexity&lt;/a&gt; for adding to a set grows at a rate of O(N), which is much better than the O(N²) from the second approach in most cases.&lt;/p&gt;
&lt;h3 id=&quot;save-memory-with-generators&quot;&gt;Save Memory With Generators&lt;/h3&gt;
&lt;p&gt;List comprehensions are convenient tools but can sometimes lead to unnecessary memory usage.&lt;/p&gt;
&lt;p&gt;Imagine you&amp;rsquo;ve been asked to find the sum of the first 1000 perfect squares, starting with 1. You know about list comprehensions, so you quickly code up a working solution:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;333833500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your solution makes a list of every perfect square between 1 and 1,000,000 and sums the values. Your code returns the right answer, but then your interviewer starts increasing the number of perfect squares you need to sum.&lt;/p&gt;
&lt;p&gt;At first, your function keeps popping out the right answer, but soon it starts slowing down until eventually the process seems to hang for an eternity. This is the last thing you want happening in a coding interview.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s going on here?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s making a list of every perfect square you&amp;rsquo;ve requested and summing them all. A list with 1000 perfect squares may not be large in computer-terms, but 100 million or 1 billion is quite a bit of information and can easily overwhelm your computer&amp;rsquo;s available memory resources. That&amp;rsquo;s what&amp;rsquo;s happening here.&lt;/p&gt;
&lt;p&gt;Thankfully, there&amp;rsquo;s a quick way to solve the memory problem. You just replace the brackets with parentheses:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;333833500&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Swapping out the brackets changes your list comprehension into a &lt;a href=&quot;https://realpython.com/introduction-to-python-generators/&quot;&gt;generator expression&lt;/a&gt;. Generator expressions are perfect for when you know you want to retrieve data from a sequence, but you don&amp;rsquo;t need to access all of it at the same time.&lt;/p&gt;
&lt;p&gt;Instead of creating a list, the generator expression returns a &lt;code&gt;generator&lt;/code&gt; object. That object knows where it is in the current state (for example, &lt;code&gt;i = 49&lt;/code&gt;) and only calculates the next value when it&amp;rsquo;s asked for.&lt;/p&gt;
&lt;p&gt;So when &lt;code&gt;sum&lt;/code&gt; iterates over the generator object by calling &lt;code&gt;.__next__()&lt;/code&gt; repeatedly, the generator checks what &lt;code&gt;i&lt;/code&gt; equals, calculates &lt;code&gt;i * i&lt;/code&gt;, increments &lt;code&gt;i&lt;/code&gt; internally, and returns the proper value to &lt;code&gt;sum&lt;/code&gt;. The design allows generators to be used on massive sequences of data, because only one element exists in memory at a time.&lt;/p&gt;
&lt;h3 id=&quot;define-default-values-in-dictionaries-with-get-and-setdefault&quot;&gt;Define Default Values in Dictionaries With &lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.setdefault()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;One of the most common programming tasks involves adding, modifying, or retrieving an item that may or may not be in a dictionary. Python dictionaries have elegant functionality to make these tasks clean and easy, but developers often check explicitly for values when it isn&amp;rsquo;t necessary.&lt;/p&gt;
&lt;p&gt;Imagine you have a dictionary named &lt;code&gt;cowboy&lt;/code&gt;, and you want to get that cowboy&amp;rsquo;s name. One approach is to explicitly check for the key with a conditional:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;age&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;horse&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;mustang&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;hat_size&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;large&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This approach first checks if the &lt;code&gt;name&lt;/code&gt; key exists in the dictionary, and if so, it returns the corresponding value. Otherwise, it returns a default value.&lt;/p&gt;
&lt;p&gt;While explicitly checking for keys does work, it can easily be replaced with one line if you use &lt;code&gt;.get()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;get()&lt;/code&gt; performs the same operations that were performed in the first approach, but now they&amp;rsquo;re handled automatically. If the key exists, then the proper value will be returned. Otherwise, the default value will get returned.&lt;/p&gt;
&lt;p&gt;But what if you want to update the dictionary with a default value while still accessing the &lt;code&gt;name&lt;/code&gt; key? &lt;code&gt;.get()&lt;/code&gt; doesn&amp;rsquo;t really help you here, so you&amp;rsquo;re left with explicitly checking for the value again:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Checking for the value and setting a default is a valid approach and is easy to read, but again Python offers a more elegant method with &lt;code&gt;.setdefault()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cowboy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setdefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;The Man with No Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.setdefault()&lt;/code&gt; accomplishes exactly the same thing as the snippet above. It checks if &lt;code&gt;name&lt;/code&gt; exists in &lt;code&gt;cowboy&lt;/code&gt;, and if so it returns that value. Otherwise, it sets &lt;code&gt;cowboy[&#39;name&#39;]&lt;/code&gt; to &lt;code&gt;The Man with No Name&lt;/code&gt; and returns the new value.&lt;/p&gt;
&lt;h2 id=&quot;take-advantage-of-pythons-standard-library&quot;&gt;Take Advantage of Python&amp;rsquo;s Standard Library&lt;/h2&gt;
&lt;p&gt;By default, Python comes with a lot of functionality that&amp;rsquo;s just an &lt;code&gt;import&lt;/code&gt; statement away. It&amp;rsquo;s powerful on its own, but knowing how to leverage the standard library can supercharge your coding interview skills.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to pick the most useful pieces from all of the available modules, so this section will focus on just a small subset of its utility functions. Hopefully, these will prove useful to you in coding interviews and also whet your appetite to learn more about the advanced functionality of these and other modules.&lt;/p&gt;
&lt;h3 id=&quot;handle-missing-dictionary-keys-with-collectionsdefaultdict&quot;&gt;Handle Missing Dictionary Keys With &lt;code&gt;collections.defaultdict()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.setdefault()&lt;/code&gt; work well when you&amp;rsquo;re setting a default for a single key, but it&amp;rsquo;s common to want a default value for all possible unset keys, especially when programming in a coding interview context.&lt;/p&gt;
&lt;p&gt;Pretend you have a group of students, and you need to keep track of their grades on homework assignments. The input value is a list of tuples with the format &lt;code&gt;(student_name, grade)&lt;/code&gt;, but you want to easily look up all the grades for a single student without iterating over the list.&lt;/p&gt;
&lt;p&gt;One way to store the grade data uses a dictionary that maps student names to lists of grades:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elliot&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;91&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;neelam&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;98&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;bianca&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;81&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;elliot&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;88&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;{&amp;#39;elliot&amp;#39;: [91, 88], &amp;#39;neelam&amp;#39;: [98], &amp;#39;bianca&amp;#39;: [81]}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this approach, you iterate over the students and check if their names are already properties in the dictionary. If not, you add them to the dictionary with an empty list as the default value. You then append their actual grades to that student&amp;rsquo;s list of grades.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s an even cleaner approach that uses a &lt;code&gt;defaultdict&lt;/code&gt;,  which extends standard &lt;code&gt;dict&lt;/code&gt; functionality to allow you to set a default value that will be operated upon if the key doesn&amp;rsquo;t exist:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;defaultdict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;student_grades&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grade&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this case, you&amp;rsquo;re creating a &lt;code&gt;defaultdict&lt;/code&gt; that uses the &lt;code&gt;list()&lt;/code&gt; constructor with no arguments as a default factory method. &lt;code&gt;list()&lt;/code&gt; with no arguments returns an empty list, so &lt;code&gt;defaultdict&lt;/code&gt; calls &lt;code&gt;list()&lt;/code&gt; if the name doesn&amp;rsquo;t exist and then allows the grade to be appended. If you want to get fancy, you could also use a lambda function as your factory value to return an arbitrary constant.&lt;/p&gt;
&lt;p&gt;Leveraging a &lt;code&gt;defaultdict&lt;/code&gt; can lead to cleaner application code because you don&amp;rsquo;t have to worry about default values at the key level. Instead, you can handle them once at the &lt;code&gt;defaultdict&lt;/code&gt; level and afterwards act as if the key is always present.&lt;/p&gt;
&lt;h3 id=&quot;count-hashable-objects-with-collectionscounter&quot;&gt;Count Hashable Objects With &lt;code&gt;collections.Counter&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You have a long string of words with no punctuation or capital letters and you want to count how many times each word appears.&lt;/p&gt;
&lt;p&gt;You could use a dictionary or &lt;code&gt;defaultdict&lt;/code&gt; and increment the counts, but &lt;code&gt;collections.Counter&lt;/code&gt; provides a cleaner and more convenient way to do exactly that. Counter is a subclass of &lt;code&gt;dict&lt;/code&gt; that uses &lt;code&gt;0&lt;/code&gt; as the default value for any missing element and makes it easier to count occurrences of objects:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;collections&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;if there was there was but if &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;there was not there was not&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Counter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Counter({&amp;#39;if&amp;#39;: 2, &amp;#39;there&amp;#39;: 4, &amp;#39;was&amp;#39;: 4, &amp;#39;not&amp;#39;: 2, &amp;#39;but&amp;#39;: 1})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you pass in the list of words to &lt;code&gt;Counter&lt;/code&gt;, it stores each word along with a count of how many times that word occurred in the list.&lt;/p&gt;
&lt;p&gt;Are you curious what the two most common words were? Just use &lt;code&gt;.most_common()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;most_common&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;there&amp;#39;, 4), (&amp;#39;was&amp;#39;, 4)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.most_common()&lt;/code&gt; is a convenience method and simply returns the &lt;code&gt;n&lt;/code&gt; most frequent inputs by count.&lt;/p&gt;
&lt;h3 id=&quot;access-common-string-groups-with-string-constants&quot;&gt;Access Common String Groups With &lt;code&gt;string&lt;/code&gt; Constants&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s trivia time! Is &lt;code&gt;&#39;A&#39; &amp;gt; &#39;a&#39;&lt;/code&gt; true or false?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s false, because the ASCII code for &lt;code&gt;A&lt;/code&gt; is 65, but &lt;code&gt;a&lt;/code&gt; is 97, and 65 is not greater than 97.&lt;/p&gt;
&lt;p&gt;Why does the answer matter? Because if you want to check if a character is part of the English alphabet, one popular way is to see if it&amp;rsquo;s between &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt; (65 and 122 on the ASCII chart).&lt;/p&gt;
&lt;p&gt;Checking the ASCII code works but is clumsy and easy to mess up in coding interviews, especially if you can&amp;rsquo;t remember whether lowercase or uppercase ASCII characters come first. It&amp;rsquo;s much easier to use the constants defined as part of the &lt;a href=&quot;https://docs.python.org/3/library/string.html&quot;&gt;&lt;code&gt;string&lt;/code&gt; module&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see one in use in &lt;code&gt;is_upper()&lt;/code&gt;, which returns whether all characters in a string are uppercase letters:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;letter&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;letter&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ascii_uppercase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Thanks Geir&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_upper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;LOL&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;is_upper()&lt;/code&gt; iterates over the letters in &lt;code&gt;word&lt;/code&gt;, and checks if the letters are part of &lt;code&gt;string.ascii_uppercase&lt;/code&gt;. If you print out &lt;code&gt;string.ascii_uppercase&lt;/code&gt; you&amp;rsquo;ll see that it&amp;rsquo;s just a lowly string. The value is set to the literal &lt;code&gt;&#39;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All &lt;code&gt;string&lt;/code&gt; constants are just strings of frequently referenced string values. They include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;string.ascii_letters&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.ascii_uppercase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.ascii_lowercase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.digits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.hexdigits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.octdigits&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.punctuation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.printable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string.whitespace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are easier to use and, even more importantly, easier to read.&lt;/p&gt;
&lt;h3 id=&quot;generate-permutations-and-combinations-with-itertools&quot;&gt;Generate Permutations and Combinations With &lt;code&gt;itertools&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Interviewers love to give real life scenarios to make coding interviews seem less intimidating, so here&amp;rsquo;s a contrived example: you go to an amusement park and decide to figure out every possible pair of friends that could sit together on a roller coaster.&lt;/p&gt;
&lt;p&gt;Unless generating these pairs is the primary purpose of the interview question, it&amp;rsquo;s likely that generating all the possible pairs is just a tedious step on the way towards a working algorithm. You could calculate them yourself with nested for-loops, or you could use the powerful &lt;a href=&quot;https://realpython.com/python-itertools/&quot;&gt;&lt;code&gt;itertools&lt;/code&gt; library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools&lt;/code&gt; has multiple tools for generating iterable sequences of input data, but right now we&amp;rsquo;ll just focus on two common functions: &lt;code&gt;itertools.permutations()&lt;/code&gt; and &lt;code&gt;itertools.combinations()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools.permutations()&lt;/code&gt; builds a list of all permutations, meaning it&amp;rsquo;s a list of every possible grouping of input values with a length matching the &lt;code&gt;count&lt;/code&gt; parameter. The &lt;code&gt;r&lt;/code&gt; keyword argument lets us specify how many values go in each grouping:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;itertools&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Monique&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Ashish&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Devon&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Bernie&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itertools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;permutations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;Monique&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Ashish&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Devon&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Bernie&amp;#39;, &amp;#39;Monique&amp;#39;), (&amp;#39;Bernie&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Bernie&amp;#39;, &amp;#39;Devon&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With permutations, the order of the elements matters, so &lt;code&gt;(&#39;sam&#39;, &#39;devon&#39;)&lt;/code&gt; represents a different pairing than &lt;code&gt;(&#39;devon&#39;, &#39;sam&#39;)&lt;/code&gt;, meaning that they would both be included in the list.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;itertools.combinations()&lt;/code&gt; builds combinations. These are also the possible groupings of the input values, but now the order of the values doesn&amp;rsquo;t matter. Because &lt;code&gt;(&#39;sam&#39;, &#39;devon&#39;)&lt;/code&gt; and &lt;code&gt;(&#39;devon&#39;, &#39;sam&#39;)&lt;/code&gt; represent the same pair, only one of them would be included in the output list:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;itertools&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;friends&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[(&amp;#39;Monique&amp;#39;, &amp;#39;Ashish&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Monique&amp;#39;, &amp;#39;Bernie&amp;#39;),&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;(&amp;#39;Ashish&amp;#39;, &amp;#39;Devon&amp;#39;), (&amp;#39;Ashish&amp;#39;, &amp;#39;Bernie&amp;#39;), (&amp;#39;Devon&amp;#39;, &amp;#39;Bernie&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since the order of the values matters with combinations, there are fewer combinations than permutations for the same input list. Again, because we set &lt;code&gt;r&lt;/code&gt; to 2, each grouping has two names in it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.combinations()&lt;/code&gt; and &lt;code&gt;.permutations()&lt;/code&gt; are just small examples of a powerful library, but even these two functions can be quite useful when you&amp;rsquo;re trying to solve an algorithm problem quickly.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-coding-interview-superpowers&quot;&gt;Conclusion: Coding Interview Superpowers&lt;/h2&gt;
&lt;p&gt;You can now feel comfortable using some of Python&amp;rsquo;s less common, but more powerful, standard features in your next coding interview. There&amp;rsquo;s a lot to learn about the language as a whole but this article should have given you a starting point to go deeper while letting you use Python more effectively when you interview.&lt;/p&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve learned different types of standard tools to supercharge your coding interview skills:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.python.org/3/library/functions.html#built-in-functions&quot;&gt;Powerful built-in functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Data structures built to handle common scenarios with barely any code&lt;/li&gt;
&lt;li&gt;Standard library packages that have feature-rich solutions for specific problems, letting you write better code faster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Interviewing may not be the best approximation of real software development, but it&amp;rsquo;s worth knowing how to succeed in any programming environment, even interviews. Thankfully, learning how to use Python during coding interviews can help you understand the language more deeply, which will pay dividends during day-to-day development.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Django Migrations 101</title>
      <id>https://realpython.com/courses/django-migrations-101/</id>
      <link href="https://realpython.com/courses/django-migrations-101/"/>
      <updated>2019-03-26T14:00:00+00:00</updated>
      <summary>With this course you’ll get comfortable with Django migrations and learn how to create database tables without writing any SQL, how to automatically modify your database after you changed your models, and how to revert changes made to your database.</summary>
      <content type="html">
        &lt;p&gt;Since version 1.7, Django has come with built-in support for database migrations. In Django, database migrations usually go hand in hand with models: whenever you code up a new model, you also generate a migration to create the necessary table in the database. However, migrations can do much more.&lt;/p&gt;
&lt;p&gt;With this course you’ll get comfortable with Django migrations and learn how to create database tables without writing any SQL, how to automatically modify your database after you changed your models, and how to revert changes made to your database.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Documenting Python Projects With Sphinx and Read The Docs</title>
      <id>https://realpython.com/courses/documenting-python-projects-sphinx-read-the-docs/</id>
      <link href="https://realpython.com/courses/documenting-python-projects-sphinx-read-the-docs/"/>
      <updated>2019-03-26T13:30:00+00:00</updated>
      <summary>In this video series we&#39;ll cover creating Python documentation from scratch using Sphinx, as well as getting your code repository hooked up to Read The Docs, to automatically build and publish your code documentation.</summary>
      <content type="html">
        &lt;p&gt;In this video series we&amp;rsquo;ll cover creating Python documentation from scratch using Sphinx, as well as getting your code repository hooked up to Read The Docs, to automatically build and publish your code documentation.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This course uses Python 2.7 in its coding examples.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/myusuf3/doctut&quot;&gt;&lt;code&gt;doctut&lt;/code&gt; Example Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://doctut.readthedocs.io/en/latest/&quot;&gt;&lt;code&gt;doctut&lt;/code&gt; Example Docs on Read The Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>An Intro to Threading in Python</title>
      <id>https://realpython.com/intro-to-python-threading/</id>
      <link href="https://realpython.com/intro-to-python-threading/"/>
      <updated>2019-03-25T14:00:00+00:00</updated>
      <summary>In this intermediate-level tutorial, you&#39;ll learn how to use threading in your Python programs. You&#39;ll see how to create threads, how to coordinate and synchronize them, and how to handle common problems that arise in threading.</summary>
      <content type="html">
        &lt;p&gt;Python threading allows you to have different parts of your program run concurrently and can simplify your design. If you&amp;rsquo;ve got some experience in Python and want to speed up your program using threads, then this tutorial is for you!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What threads are&lt;/li&gt;
&lt;li&gt;How to create threads and wait for them to finish&lt;/li&gt;
&lt;li&gt;How to use a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;How to avoid race conditions&lt;/li&gt;
&lt;li&gt;How to use the common tools that Python &lt;code&gt;threading&lt;/code&gt; provides&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article assumes you&amp;rsquo;ve got the Python basics down pat and that you&amp;rsquo;re using at least version 3.6 to run the examples. If you need a refresher, you can start with the &lt;a href=&quot;https://realpython.com/learning-paths/&quot;&gt;Python Learning Paths&lt;/a&gt; and get up to speed.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not sure if you want to use Python &lt;code&gt;threading&lt;/code&gt;, &lt;code&gt;asyncio&lt;/code&gt;, or &lt;code&gt;multiprocessing&lt;/code&gt;, then you can check out &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed Up Your Python Program With Concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of the sources used in this tutorial are available to you in the &lt;a href=&quot;https://github.com/realpython/materials/tree/master/intro-to-threading&quot;&gt;&lt;em&gt;Real Python&lt;/em&gt; GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;i class=&quot;fa fa-graduation-cap&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Threading” quiz. Upon completion you will receive a score so you can track your learning progress over time:&lt;/p&gt;&lt;p class=&quot;text-center my-2&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;/quizzes/python-threading/&quot; target=&quot;_blank&quot;&gt;Take the Quiz »&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;what-is-a-thread&quot;&gt;What Is a Thread?&lt;/h2&gt;
&lt;p&gt;A thread is a separate flow of execution. This means that your program will have two things happening at once. But for most Python 3 implementations the different threads do not actually execute at the same time: they merely appear to.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s tempting to think of threading as having two (or more) different processors running on your program, each one doing an independent task at the same time. That&amp;rsquo;s almost right. The threads may be running on different processors, but they will only be running one at a time. &lt;/p&gt;
&lt;p&gt;Getting multiple tasks running simultaneously requires a non-standard implementation of Python, writing some of your code in a different language, or using &lt;code&gt;multiprocessing&lt;/code&gt; which comes with some extra overhead.&lt;/p&gt;
&lt;p&gt;Because of the way CPython implementation of Python works, threading may not speed up all tasks.  This is due to interactions with the &lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;GIL&lt;/a&gt; that essentially limit one Python thread to run at a time. &lt;/p&gt;
&lt;p&gt;Tasks that spend much of their time waiting for external events are generally good candidates for threading.  Problems that require heavy CPU computation and spend little time waiting for external events might not run faster at all.  &lt;/p&gt;
&lt;p&gt;This is true for code written in Python and running on the standard CPython implementation.  If your threads are written in C they have the ability to release the GIL and run concurrently.  If you are running on a different Python implementation, check with the documentation too see how it handles threads.  &lt;/p&gt;
&lt;p&gt;If you are running a standard Python implementation, writing in only Python, and have a CPU-bound problem, you should check out the &lt;code&gt;multiprocessing&lt;/code&gt; module instead.&lt;/p&gt;
&lt;p&gt;Architecting your program to use threading can also provide gains in design clarity. Most of the examples you&amp;rsquo;ll learn about in this tutorial are not necessarily going to run faster because they use threads. Using threading in them helps to make the design cleaner and easier to reason about.&lt;/p&gt;
&lt;p&gt;So, let&amp;rsquo;s stop talking about threading and start using it!&lt;/p&gt;
&lt;h2 id=&quot;starting-a-thread&quot;&gt;Starting a Thread&lt;/h2&gt;
&lt;p&gt;Now that you&amp;rsquo;ve got an idea of what a thread is, let&amp;rsquo;s learn how to make one. The Python standard library provides &lt;a href=&quot;https://docs.python.org/3/library/threading.html&quot;&gt;&lt;code&gt;threading&lt;/code&gt;&lt;/a&gt;, which contains most of the primitives you&amp;rsquo;ll see in this article. &lt;code&gt;Thread&lt;/code&gt;, in this module, nicely encapsulates threads, providing a clean interface to work with them.&lt;/p&gt;
&lt;p&gt;To start a separate thread, you create a &lt;code&gt;Thread&lt;/code&gt; instance and then tell it to &lt;code&gt;.start()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before creating thread&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;16 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;17 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before running thread&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;18 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;19 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : wait for the thread to finish&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;20 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# x.join()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;21 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : all done&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you look around the logging statements, you can see that the &lt;code&gt;main&lt;/code&gt; section is creating and starting the thread:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you create a &lt;code&gt;Thread&lt;/code&gt;, you pass it a function and a list containing the arguments to that function. In this case, you&amp;rsquo;re telling the &lt;code&gt;Thread&lt;/code&gt; to run &lt;code&gt;thread_function()&lt;/code&gt; and to pass it &lt;code&gt;1&lt;/code&gt; as an argument.&lt;/p&gt;
&lt;p&gt;For this article, you&amp;rsquo;ll use sequential integers as names for your threads. There is &lt;code&gt;threading.get_ident()&lt;/code&gt;, which returns a unique name for each thread, but these are usually neither short nor easily readable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;thread_function()&lt;/code&gt; itself doesn&amp;rsquo;t do much. It simply logs some messages with a &lt;code&gt;time.sleep()&lt;/code&gt; in between them.&lt;/p&gt;
&lt;p&gt;When you run this program as it is (with line twenty commented out), the output will look like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./single_thread.py
&lt;span class=&quot;go&quot;&gt;Main    : before creating thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before running thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : wait for the thread to finish&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : all done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ll notice that the &lt;code&gt;Thread&lt;/code&gt; finished after the &lt;code&gt;Main&lt;/code&gt; section of your code did. You&amp;rsquo;ll come back to why that is and talk about the mysterious line twenty in the next section.&lt;/p&gt;
&lt;h3 id=&quot;daemon-threads&quot;&gt;Daemon Threads&lt;/h3&gt;
&lt;p&gt;In computer science, a &lt;a href=&quot;https://en.wikipedia.org/wiki/Daemon_(computing)&quot;&gt;&lt;code&gt;daemon&lt;/code&gt;&lt;/a&gt; is a process that runs in the background.&lt;/p&gt;
&lt;p&gt;Python &lt;code&gt;threading&lt;/code&gt; has a more specific meaning for &lt;code&gt;daemon&lt;/code&gt;. A &lt;code&gt;daemon&lt;/code&gt; thread will shut down immediately when the program exits. One way to think about these definitions is to consider the &lt;code&gt;daemon&lt;/code&gt; thread a thread that runs in the background without worrying about shutting it down.&lt;/p&gt;
&lt;p&gt;If a program is running &lt;code&gt;Threads&lt;/code&gt; that are not &lt;code&gt;daemons&lt;/code&gt;, then the program will wait for those threads to complete before it terminates. &lt;code&gt;Threads&lt;/code&gt; that &lt;em&gt;are&lt;/em&gt; daemons, however, are just killed wherever they are when the program is exiting.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look a little more closely at the output of your program above. The last two lines are the interesting bit. When you run the program, you&amp;rsquo;ll notice that there is a pause (of about 2 seconds) after &lt;code&gt;__main__&lt;/code&gt; has printed its &lt;code&gt;all done&lt;/code&gt; message and before the thread is finished.&lt;/p&gt;
&lt;p&gt;This pause is Python waiting for the non-daemonic thread to complete. When your Python program ends, part of the shutdown process is to clean up the threading routine.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;a href=&quot;https://github.com/python/cpython/blob/df5cdc11123a35065bbf1636251447d0bfe789a5/Lib/threading.py#L1263&quot;&gt;source for Python &lt;code&gt;threading&lt;/code&gt;&lt;/a&gt;, you&amp;rsquo;ll see that &lt;code&gt;threading._shutdown()&lt;/code&gt; walks through all of the running threads and calls &lt;code&gt;.join()&lt;/code&gt; on every one that does not have the &lt;code&gt;daemon&lt;/code&gt; flag set.&lt;/p&gt;
&lt;p&gt;So your program waits to exit because the thread itself is waiting in a sleep. As soon as it has completed and printed the message, &lt;code&gt;.join()&lt;/code&gt; will return and the program can exit.&lt;/p&gt;
&lt;p&gt;Frequently, this behavior is what you want, but there are other options available to us. Let&amp;rsquo;s first repeat the program with a &lt;code&gt;daemon&lt;/code&gt; thread. You do that by changing how you construct the &lt;code&gt;Thread&lt;/code&gt;, adding the &lt;code&gt;daemon=True&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;daemon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run the program now, you should see this output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./daemon_thread.py
&lt;span class=&quot;go&quot;&gt;Main    : before creating thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before running thread&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : wait for the thread to finish&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : all done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The difference here is that the final line of the output is missing. &lt;code&gt;thread_function()&lt;/code&gt; did not get a chance to complete. It was a &lt;code&gt;daemon&lt;/code&gt; thread, so when &lt;code&gt;__main__&lt;/code&gt; reached the end of its code and the program wanted to finish, the daemon was killed.&lt;/p&gt;
&lt;h3 id=&quot;join-a-thread&quot;&gt;&lt;code&gt;join()&lt;/code&gt; a Thread&lt;/h3&gt;
&lt;p&gt;Daemon threads are handy, but what about when you want to wait for a thread to stop? What about when you want to do that and not exit your program? Now let&amp;rsquo;s go back to your original program and look at that commented out line twenty:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# x.join()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To tell one thread to wait for another thread to finish, you call &lt;code&gt;.join()&lt;/code&gt;. If you uncomment that line, the main thread will pause and wait for the thread &lt;code&gt;x&lt;/code&gt; to complete running.&lt;/p&gt;
&lt;p&gt;Did you test this on the code with the daemon thread or the regular thread? It turns out that it doesn&amp;rsquo;t matter. If you &lt;code&gt;.join()&lt;/code&gt; a thread, that statement will wait until either kind of thread is finished.&lt;/p&gt;
&lt;h2 id=&quot;working-with-many-threads&quot;&gt;Working With Many Threads&lt;/h2&gt;
&lt;p&gt;The example code so far has only been working with two threads: the main thread and one you started with the &lt;code&gt;threading.Thread&lt;/code&gt; object. &lt;/p&gt;
&lt;p&gt;Frequently, you&amp;rsquo;ll want to start a number of threads and have them do interesting work. Let&amp;rsquo;s start by looking at the harder way of doing that, and then you&amp;rsquo;ll move on to an easier method.&lt;/p&gt;
&lt;p&gt;The harder way of starting multiple threads is the one you already know:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : create and start thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;threads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : before joining thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;thread&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main    : thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; done&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code uses the same mechanism you saw above to start a thread, create a &lt;code&gt;Thread&lt;/code&gt; object, and then call &lt;code&gt;.start()&lt;/code&gt;. The program keeps a list of &lt;code&gt;Thread&lt;/code&gt; objects so that it can then wait for them later using &lt;code&gt;.join()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Running this code multiple times will likely produce some interesting results. Here&amp;rsquo;s an example output from my machine:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./multiple_threads.py
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : create and start thread 2.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 0 done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 1.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 1 done&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : before joining thread 2.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main    : thread 2 done&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you walk through the output carefully, you&amp;rsquo;ll see all three threads getting started in the order you might expect, but in this case they finish in the opposite order! Multiple runs will produce different orderings. Look for the &lt;code&gt;Thread x: finishing&lt;/code&gt; message to tell you when each thread is done.&lt;/p&gt;
&lt;p&gt;The order in which threads are run is determined by the operating system and can be quite hard to predict. It may (and likely will) vary from run to run, so you need to be aware of that when you design algorithms that use threading.&lt;/p&gt;
&lt;p&gt;Fortunately, Python gives you several primitives that you&amp;rsquo;ll look at later to help coordinate threads and get them running together. Before that, let&amp;rsquo;s look at how to make managing a group of threads a bit easier.&lt;/p&gt;
&lt;h2 id=&quot;using-a-threadpoolexecutor&quot;&gt;Using a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s an easier way to start up a group of threads than the one you saw above. It&amp;rsquo;s called a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;, and it&amp;rsquo;s part of the standard library in &lt;a href=&quot;https://docs.python.org/3/library/concurrent.futures.html&quot;&gt;&lt;code&gt;concurrent.futures&lt;/code&gt;&lt;/a&gt; (as of Python 3.2).&lt;/p&gt;
&lt;p&gt;The easiest way to create it is as a context manager, using the &lt;code&gt;with&lt;/code&gt; statement to manage the creation and destruction of the pool.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the &lt;code&gt;__main__&lt;/code&gt; from the last example rewritten to use a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concurrent.futures&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# [rest of code]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thread_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The code creates a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; as a context manager, telling it how many worker threads it wants in the pool. It then uses &lt;code&gt;.map()&lt;/code&gt; to step through an iterable of things, in your case &lt;code&gt;range(3)&lt;/code&gt;, passing each one to a thread in the pool.&lt;/p&gt;
&lt;p&gt;The end of the &lt;code&gt;with&lt;/code&gt; block causes the &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to do a &lt;code&gt;.join()&lt;/code&gt; on each of the threads in the pool. It is &lt;em&gt;strongly&lt;/em&gt; recommended that you use &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; as a context manager when you can so that you never forget to &lt;code&gt;.join()&lt;/code&gt; the threads.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Using a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; can cause some confusing errors. &lt;/p&gt;
&lt;p&gt;For example, if you call a function that takes no parameters, but you pass it parameters in &lt;code&gt;.map()&lt;/code&gt;, the thread will throw an exception.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; will hide that exception, and (in the case above) the program terminates with no output. This can be quite confusing to debug at first.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Running your corrected example code will produce output that looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./executor.py
&lt;span class=&quot;go&quot;&gt;Thread 0: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: starting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 2: finishing&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, notice how &lt;code&gt;Thread 1&lt;/code&gt; finished before &lt;code&gt;Thread 0&lt;/code&gt;. The scheduling of threads is done by the operating system and does not follow a plan that&amp;rsquo;s easy to figure out.&lt;/p&gt;
&lt;h2 id=&quot;race-conditions&quot;&gt;Race Conditions&lt;/h2&gt;
&lt;p&gt;Before you move on to some of the other features tucked away in Python &lt;code&gt;threading&lt;/code&gt;, let&amp;rsquo;s talk a bit about one of the more difficult issues you&amp;rsquo;ll run into when writing threaded programs: &lt;a href=&quot;https://en.wikipedia.org/wiki/Race_condition&quot;&gt;race conditions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve seen what a race condition is and looked at one happening, you&amp;rsquo;ll move on to some of the primitives provided by the standard library to prevent race conditions from happening.&lt;/p&gt;
&lt;p&gt;Race conditions can occur when two or more threads access a shared piece of data or resource. In this example, you&amp;rsquo;re going to create a large race condition that happens every time, but be aware that most race conditions are not this obvious. Frequently, they only occur rarely, and they can produce confusing results. As you can imagine, this makes them quite difficult to debug.&lt;/p&gt;
&lt;p&gt;Fortunately, this race condition will happen every time, and you&amp;rsquo;ll walk through it in detail to explain what is happening.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;re going to write a class that updates a database. Okay, you&amp;rsquo;re not really going to have a database: you&amp;rsquo;re just going to fake it, because that&amp;rsquo;s not the point of this article.&lt;/p&gt;
&lt;p&gt;Your &lt;code&gt;FakeDatabase&lt;/code&gt; will have &lt;code&gt;.__init__()&lt;/code&gt; and &lt;code&gt;.update()&lt;/code&gt; methods:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;FakeDatabase&lt;/code&gt; is keeping track of a single number: &lt;code&gt;.value&lt;/code&gt;. This is going to be the shared data on which you&amp;rsquo;ll see the race condition.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.__init__()&lt;/code&gt; simply initializes &lt;code&gt;.value&lt;/code&gt; to zero. So far, so good.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.update()&lt;/code&gt; looks a little strange. It&amp;rsquo;s simulating reading a value from a database, doing some computation on it, and then writing a new value back to the database.&lt;/p&gt;
&lt;p&gt;In this case, reading from the database just means copying &lt;code&gt;.value&lt;/code&gt; to a local variable. The computation is just to add one to the value and then &lt;code&gt;.sleep()&lt;/code&gt; for a little bit. Finally, it writes the value back by copying the local value back to &lt;code&gt;.value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how you&amp;rsquo;ll use this &lt;code&gt;FakeDatabase&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Testing update. Starting value is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Testing update. Ending value is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The program creates a &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; with two threads and then calls &lt;code&gt;.submit()&lt;/code&gt; on each of them, telling them to run &lt;code&gt;database.update()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.submit()&lt;/code&gt; has a signature that allows both positional and named arguments to be passed to the function running in the thread:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the usage above, &lt;code&gt;index&lt;/code&gt; is passed as the first and only positional argument to &lt;code&gt;database.update()&lt;/code&gt;. You&amp;rsquo;ll see later in this article where you can pass multiple arguments in a similar manner.&lt;/p&gt;
&lt;p&gt;Since each thread runs &lt;code&gt;.update()&lt;/code&gt;, and &lt;code&gt;.update()&lt;/code&gt; adds one to &lt;code&gt;.value&lt;/code&gt;, you might expect &lt;code&gt;database.value&lt;/code&gt; to be &lt;code&gt;2&lt;/code&gt; when it&amp;rsquo;s printed out at the end. But you wouldn&amp;rsquo;t be looking at this example if that was the case. If you run the above code, the output looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./racecond.py
&lt;span class=&quot;go&quot;&gt;Testing unlocked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing unlocked update. Ending value is 1.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You might have expected that to happen, but let&amp;rsquo;s look at the details of what&amp;rsquo;s really going on here, as that will make the solution to this problem easier to understand.&lt;/p&gt;
&lt;h3 id=&quot;one-thread&quot;&gt;One Thread&lt;/h3&gt;
&lt;p&gt;Before you dive into this issue with two threads, let&amp;rsquo;s step back and talk a bit about some details of how threads work.&lt;/p&gt;
&lt;p&gt;You won&amp;rsquo;t be diving into all of the details here, as that&amp;rsquo;s not important at this level. We&amp;rsquo;ll also be simplifying a few things in a way that won&amp;rsquo;t be technically accurate but will give you the right idea of what is happening.&lt;/p&gt;
&lt;p&gt;When you tell your &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to run each thread, you tell it which function to run and what parameters to pass to it: &lt;code&gt;executor.submit(database.update, index)&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;The result of this is that each of the threads in the pool will call &lt;code&gt;database.update(index)&lt;/code&gt;. Note that &lt;code&gt;database&lt;/code&gt; is a reference to the one &lt;code&gt;FakeDatabase&lt;/code&gt; object created in &lt;code&gt;__main__&lt;/code&gt;. Calling &lt;code&gt;.update()&lt;/code&gt; on that object calls an &lt;a href=&quot;https://realpython.com/instance-class-and-static-methods-demystified/&quot;&gt;instance method&lt;/a&gt; on that object.&lt;/p&gt;
&lt;p&gt;Each thread is going to have a reference to the same &lt;code&gt;FakeDatabase&lt;/code&gt; object, &lt;code&gt;database&lt;/code&gt;. Each thread will also have a unique value, &lt;code&gt;index&lt;/code&gt;, to make the logging statements a bit easier to read:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&quot; width=&quot;1623&quot; height=&quot;663&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&amp;amp;w=405&amp;amp;sig=a2cf6c8e812b6c948114c50b5402d51fb25ed155 405w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png&amp;amp;w=811&amp;amp;sig=3708e9d6821e459c00fe576ffaa01cd2cab14b70 811w, https://files.realpython.com/media/intro-threading-shared-database.267a5d8c6aa1.png 1623w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 1 and Thread 2 use the same shared database.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When the thread starts running &lt;code&gt;.update()&lt;/code&gt;, it has its own version of all of the data &lt;strong&gt;local&lt;/strong&gt; to the function. In the case of &lt;code&gt;.update()&lt;/code&gt;, this is &lt;code&gt;local_copy&lt;/code&gt;. This is definitely a good thing. Otherwise, two threads running the same function would always confuse each other. It means that all variables that are scoped (or local) to a function are &lt;strong&gt;thread-safe&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Now you can start walking through what happens if you run the program above with a single thread and a single call to &lt;code&gt;.update()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The image below steps through the execution of &lt;code&gt;.update()&lt;/code&gt; if only a single thread is run. The statement is shown on the left followed by a diagram showing the values in the thread&amp;rsquo;s &lt;code&gt;local_value&lt;/code&gt; and the shared &lt;code&gt;database.value&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&quot; width=&quot;1383&quot; height=&quot;2901&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&amp;amp;w=345&amp;amp;sig=3964a752874e9128c41941a52f9c7342e2f1e12a 345w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-single-thread.85204fa11210.png&amp;amp;w=691&amp;amp;sig=552252777e57ea21cc6aaa6ed39263aa1e60c1da 691w, https://files.realpython.com/media/intro-threading-single-thread.85204fa11210.png 1383w&quot; sizes=&quot;75vw&quot; alt=&quot;Single thread modifying a shared database&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The diagram is laid out so that time increases as you move from top to bottom. It begins when &lt;code&gt;Thread 1&lt;/code&gt; is created and ends when it is terminated.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; starts, &lt;code&gt;FakeDatabase.value&lt;/code&gt; is zero. The first line of code in the method, &lt;code&gt;local_copy = self.value&lt;/code&gt;, copies the value zero to the local variable. Next it increments the value of &lt;code&gt;local_copy&lt;/code&gt; with the &lt;code&gt;local_copy += 1&lt;/code&gt; statement. You can see &lt;code&gt;.value&lt;/code&gt; in &lt;code&gt;Thread 1&lt;/code&gt; getting set to one.&lt;/p&gt;
&lt;p&gt;Next &lt;code&gt;time.sleep()&lt;/code&gt; is called, which makes the current thread pause and allows other threads to run. Since there is only one thread in this example, this has no effect.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; wakes up and continues, it copies the new value from &lt;code&gt;local_copy&lt;/code&gt; to &lt;code&gt;FakeDatabase.value&lt;/code&gt;, and then the thread is complete. You can see that &lt;code&gt;database.value&lt;/code&gt; is set to one.&lt;/p&gt;
&lt;p&gt;So far, so good. You ran &lt;code&gt;.update()&lt;/code&gt; once and &lt;code&gt;FakeDatabase.value&lt;/code&gt; was incremented to one.&lt;/p&gt;
&lt;h3 id=&quot;two-threads&quot;&gt;Two Threads&lt;/h3&gt;
&lt;p&gt;Getting back to the race condition, the two threads will be running concurrently but not at the same time. They will each have their own version of &lt;code&gt;local_copy&lt;/code&gt; and will each point to the same &lt;code&gt;database&lt;/code&gt;. It is this shared &lt;code&gt;database&lt;/code&gt; object that is going to cause the problems.&lt;/p&gt;
&lt;p&gt;The program starts with &lt;code&gt;Thread 1&lt;/code&gt; running &lt;code&gt;.update()&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&quot; width=&quot;1953&quot; height=&quot;1641&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&amp;amp;w=488&amp;amp;sig=5a03e51ef3b111ab7525d287bfc56571927da0db 488w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png&amp;amp;w=976&amp;amp;sig=fe92c3576eefae044f17a55010816e0d1c9ec240 976w, https://files.realpython.com/media/intro-threading-two-threads-part1.c1c0e65a8481.png 1953w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 1 gets a copy of shared data and increments it.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 1&lt;/code&gt; calls &lt;code&gt;time.sleep()&lt;/code&gt;, it allows the other thread to start running. This is where things get interesting.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Thread 2&lt;/code&gt; starts up and does the same operations. It&amp;rsquo;s also copying &lt;code&gt;database.value&lt;/code&gt; into its private &lt;code&gt;local_copy&lt;/code&gt;, and this shared &lt;code&gt;database.value&lt;/code&gt; has not yet been updated:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&quot; width=&quot;2013&quot; height=&quot;1641&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&amp;amp;w=503&amp;amp;sig=469e3f134c8c24d34a4923a3afa7c4261b199935 503w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png&amp;amp;w=1006&amp;amp;sig=59d3261bd00bf21276180b03684a5b36cc949c01 1006w, https://files.realpython.com/media/intro-threading-two-threads-part2.df42d4fbfe21.png 2013w&quot; sizes=&quot;75vw&quot; alt=&quot;Thread 2 gets a copy of shared data and increments it.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Thread 2&lt;/code&gt; finally goes to sleep, the shared &lt;code&gt;database.value&lt;/code&gt; is still unmodified at zero, and both private versions of &lt;code&gt;local_copy&lt;/code&gt; have the value one.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Thread 1&lt;/code&gt; now wakes up and saves its version of &lt;code&gt;local_copy&lt;/code&gt; and then terminates, giving &lt;code&gt;Thread 2&lt;/code&gt; a final chance to run. &lt;code&gt;Thread 2&lt;/code&gt; has no idea that &lt;code&gt;Thread 1&lt;/code&gt; ran and updated &lt;code&gt;database.value&lt;/code&gt; while it was sleeping. It stores &lt;em&gt;its&lt;/em&gt; version of &lt;code&gt;local_copy&lt;/code&gt; into &lt;code&gt;database.value&lt;/code&gt;, also setting it to one:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&quot; width=&quot;2013&quot; height=&quot;1551&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&amp;amp;w=503&amp;amp;sig=3b11ef66873019a9edf48a7ff7445bc60d6daada 503w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png&amp;amp;w=1006&amp;amp;sig=bb0878750a37d23cfca07f30e95f289b1ee82770 1006w, https://files.realpython.com/media/intro-threading-two-threads-part3.18576920f88f.png 2013w&quot; sizes=&quot;75vw&quot; alt=&quot;Both threads write 1 to shared database.&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The two threads have interleaving access to a single shared object, overwriting each other&amp;rsquo;s results. Similar race conditions can arise when one thread frees memory or closes a file handle before the other thread is finished accessing it.&lt;/p&gt;
&lt;h3 id=&quot;why-this-isnt-a-silly-example&quot;&gt;Why This Isn&amp;rsquo;t a Silly Example&lt;/h3&gt;
&lt;p&gt;The example above is contrived to make sure that the race condition happens every time you run your program. Because the operating system can swap out a thread at any time, it is possible to interrupt a statement like &lt;code&gt;x = x + 1&lt;/code&gt; after it has read the value of &lt;code&gt;x&lt;/code&gt; but before it has written back the incremented value.&lt;/p&gt;
&lt;p&gt;The details of how this happens are quite interesting, but not needed for the rest of this article, so feel free to skip over this hidden section.&lt;/p&gt;
&lt;div class=&quot;card mb-3&quot; id=&quot;collapse_carda9325a&quot;&gt;
&lt;div class=&quot;card-header border-0&quot;&gt;&lt;p class=&quot;m-0&quot;&gt;&lt;button class=&quot;btn&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapsea9325a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsea9325a&quot;&gt;How Does This Really Work&lt;/button&gt; &lt;button class=&quot;btn btn-link float-right&quot; data-toggle=&quot;collapse&quot; data-target=&quot;#collapsea9325a&quot; aria-expanded=&quot;false&quot; aria-controls=&quot;collapsea9325a&quot;&gt;Show/Hide&lt;/button&gt;&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;collapsea9325a&quot; class=&quot;collapse&quot; data-parent=&quot;#collapse_carda9325a&quot;&gt;&lt;div class=&quot;card-body&quot; markdown=&quot;1&quot;&gt;

&lt;p&gt;The code above isn&amp;rsquo;t quite as out there as you might originally have thought. It was designed to force a race condition every time you run it, but that makes it much easier to solve than most race conditions.&lt;/p&gt;
&lt;p&gt;There are two things to keep in mind when thinking about race conditions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Even an operation like &lt;code&gt;x += 1&lt;/code&gt; takes the processor many steps. Each of these steps is a separate instruction to the processor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The operating system can swap which thread is running &lt;em&gt;at any time&lt;/em&gt;. A thread can be swapped out after any of these small instructions. This means that a thread can be put to sleep to let another thread run in the &lt;em&gt;middle&lt;/em&gt; of a Python statement.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s look at this in detail. The REPL below shows a function that takes a parameter and increments it:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dis&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2           0 LOAD_FAST                0 (x)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              2 LOAD_CONST               1 (1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              4 INPLACE_ADD&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              6 STORE_FAST               0 (x)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;              8 LOAD_CONST               0 (None)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;             10 RETURN_VALUE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The REPL example uses &lt;a href=&quot;https://docs.python.org/3/library/dis.html&quot;&gt;&lt;code&gt;dis&lt;/code&gt;&lt;/a&gt; from the Python standard library to show the smaller steps that the processor does to implement your function. It does a &lt;code&gt;LOAD_FAST&lt;/code&gt; of the data value &lt;code&gt;x&lt;/code&gt;, it does a &lt;code&gt;LOAD_CONST 1&lt;/code&gt;, and then it uses the &lt;code&gt;INPLACE_ADD&lt;/code&gt; to add those values together.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re stopping here for a specific reason. This is the point in &lt;code&gt;.update()&lt;/code&gt; above where &lt;code&gt;time.sleep()&lt;/code&gt; forced the threads to switch. It is entirely possible that, every once in while, the operating system would switch threads at that exact point even without &lt;code&gt;sleep()&lt;/code&gt;, but the call to &lt;code&gt;sleep()&lt;/code&gt; makes it happen every time.&lt;/p&gt;
&lt;p&gt;As you learned above, the operating system can swap threads at any time. You&amp;rsquo;ve walked down this listing to the statement marked &lt;code&gt;4&lt;/code&gt;. If the operating system swaps out this thread and runs a different thread that also modifies &lt;code&gt;x&lt;/code&gt;, then when this thread resumes, it will overwrite &lt;code&gt;x&lt;/code&gt; with an incorrect value.&lt;/p&gt;
&lt;p&gt;Technically, this example won&amp;rsquo;t have a race condition because &lt;code&gt;x&lt;/code&gt; is local to &lt;code&gt;inc()&lt;/code&gt;. It does illustrate how a thread can be interrupted during a single Python operation, however. The same LOAD, MODIFY, STORE set of operations also happens on global and shared values. You can explore with the &lt;code&gt;dis&lt;/code&gt; module and prove that yourself.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s rare to get a race condition like this to occur, but remember that an infrequent event taken over millions of iterations becomes likely to happen. The rarity of these race conditions makes them much, much harder to debug than regular bugs.&lt;/p&gt;
&lt;p&gt;Now back to your regularly scheduled tutorial!&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;Now that you&amp;rsquo;ve seen a race condition in action, let&amp;rsquo;s find out how to solve them!&lt;/p&gt;
&lt;h2 id=&quot;basic-synchronization-using-lock&quot;&gt;Basic Synchronization Using &lt;code&gt;Lock&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of ways to avoid or solve race conditions. You won&amp;rsquo;t look at all of them here, but there are a couple that are used frequently. Let&amp;rsquo;s start with &lt;code&gt;Lock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To solve your race condition above, you need to find a way to allow only one thread at a time into the read-modify-write section of your code. The most common way to do this is called &lt;code&gt;Lock&lt;/code&gt; in Python. In some other languages this same idea is called  a &lt;code&gt;mutex&lt;/code&gt;. Mutex comes from MUTual EXclusion, which is exactly what a &lt;code&gt;Lock&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Lock&lt;/code&gt; is an object that acts like a hall pass. Only one thread at a time can have the &lt;code&gt;Lock&lt;/code&gt;. Any other thread that wants the &lt;code&gt;Lock&lt;/code&gt; must wait until the owner of the &lt;code&gt;Lock&lt;/code&gt; gives it up.&lt;/p&gt;
&lt;p&gt;The basic functions to do this are &lt;code&gt;.acquire()&lt;/code&gt; and &lt;code&gt;.release()&lt;/code&gt;. A thread will call &lt;code&gt;my_lock.acquire()&lt;/code&gt; to get the lock. If the lock is already held, the calling thread will wait until it is released. There&amp;rsquo;s an important point here. If one thread gets the lock but never gives it back, your program will be stuck. You&amp;rsquo;ll read more about this later.&lt;/p&gt;
&lt;p&gt;Fortunately, Python&amp;rsquo;s &lt;code&gt;Lock&lt;/code&gt; will also operate as a context manager, so you can use it in a &lt;code&gt;with&lt;/code&gt; statement, and it gets released automatically when the &lt;code&gt;with&lt;/code&gt; block exits for any reason.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at the &lt;code&gt;FakeDatabase&lt;/code&gt; with a &lt;code&gt;Lock&lt;/code&gt; added to it. The calling function stays the same:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FakeDatabase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;locked_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: starting update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; about to lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; has lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;local_copy&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; about to release lock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; after release&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Thread &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: finishing update&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Other than adding a bunch of debug logging so you can see the locking more clearly, the big change here is to add a member called &lt;code&gt;._lock&lt;/code&gt;, which is a &lt;code&gt;threading.Lock()&lt;/code&gt; object. This &lt;code&gt;._lock&lt;/code&gt; is initialized in the unlocked state and locked and released by the &lt;code&gt;with&lt;/code&gt; statement.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s worth noting here that the thread running this function will hold on to that &lt;code&gt;Lock&lt;/code&gt; until it is completely finished updating the database. In this case, that means it will hold the &lt;code&gt;Lock&lt;/code&gt; while it copies, updates, sleeps, and then writes the value back to the database.&lt;/p&gt;
&lt;p&gt;If you run this version with logging set to warning level, you&amp;rsquo;ll see this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./fixrace.py
&lt;span class=&quot;go&quot;&gt;Testing locked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing locked update. Ending value is 2.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Look at that. Your program finally works!&lt;/p&gt;
&lt;p&gt;You can turn on full logging by setting the level to &lt;code&gt;DEBUG&lt;/code&gt; by adding this statement after you configure the logging output in &lt;code&gt;__main__&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getLogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setLevel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running this program with &lt;code&gt;DEBUG&lt;/code&gt; logging turned on looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./fixrace.py
&lt;span class=&quot;go&quot;&gt;Testing locked update. Starting value is 0.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 about to lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 has lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: starting update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 about to lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 about to release lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0 after release&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 0: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 has lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 about to release lock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1 after release&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Thread 1: finishing update&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Testing locked update. Ending value is 2.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this output you can see &lt;code&gt;Thread 0&lt;/code&gt; acquires the lock and is still holding it when it goes to sleep. &lt;code&gt;Thread 1&lt;/code&gt; then starts and attempts to acquire the same lock. Because &lt;code&gt;Thread 0&lt;/code&gt; is still holding it, &lt;code&gt;Thread 1&lt;/code&gt; has to wait. This is the mutual exclusion that a &lt;code&gt;Lock&lt;/code&gt; provides.&lt;/p&gt;
&lt;p&gt;Many of the examples in the rest of this article will have &lt;code&gt;WARNING&lt;/code&gt; and &lt;code&gt;DEBUG&lt;/code&gt; level logging. We&amp;rsquo;ll generally only show the &lt;code&gt;WARNING&lt;/code&gt; level output, as the &lt;code&gt;DEBUG&lt;/code&gt; logs can be quite lengthy. Try out the programs with the logging turned up and see what they do.&lt;/p&gt;
&lt;h2 id=&quot;deadlock&quot;&gt;Deadlock&lt;/h2&gt;
&lt;p&gt;Before you move on, you should look at a common problem when using &lt;code&gt;Locks&lt;/code&gt;. As you saw, if the &lt;code&gt;Lock&lt;/code&gt; has already been acquired, a second call to &lt;code&gt;.acquire()&lt;/code&gt; will wait until the thread that is holding the &lt;code&gt;Lock&lt;/code&gt; calls &lt;code&gt;.release()&lt;/code&gt;. What do you think happens when you run this code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;before first acquire&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;before second acquire&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;acquired lock twice&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the program calls &lt;code&gt;l.acquire()&lt;/code&gt; the second time, it hangs waiting for the &lt;code&gt;Lock&lt;/code&gt; to be released. In this example, you can fix the deadlock by removing the second call, but deadlocks usually happen from one of two subtle things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An implementation bug where a &lt;code&gt;Lock&lt;/code&gt; is not released properly&lt;/li&gt;
&lt;li&gt;A design issue where a utility function needs to be called by functions that might or might not already have the &lt;code&gt;Lock&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first situation happens sometimes, but using a &lt;code&gt;Lock&lt;/code&gt; as a context manager greatly reduces how often. It is recommended to write code whenever possible to make use of context managers, as they help to avoid situations where an exception skips you over the &lt;code&gt;.release()&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;The design issue can be a bit trickier in some languages. Thankfully, Python threading has a second object, called &lt;code&gt;RLock&lt;/code&gt;, that is designed for just this situation. It allows a thread to &lt;code&gt;.acquire()&lt;/code&gt; an &lt;code&gt;RLock&lt;/code&gt; multiple times before it calls &lt;code&gt;.release()&lt;/code&gt;. That thread is still required to call &lt;code&gt;.release()&lt;/code&gt; the same number of times it called &lt;code&gt;.acquire()&lt;/code&gt;, but it should be doing that anyway.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Lock&lt;/code&gt; and &lt;code&gt;RLock&lt;/code&gt; are two of the basic tools used in threaded programming to prevent race conditions. There are a few other that work in different ways. Before you look at them, let&amp;rsquo;s shift to a slightly different problem domain.&lt;/p&gt;
&lt;h2 id=&quot;producer-consumer-threading&quot;&gt;Producer-Consumer Threading&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem&quot;&gt;Producer-Consumer Problem&lt;/a&gt; is a standard computer science problem used to look at threading or process synchronization issues. You&amp;rsquo;re going to look at a variant of it to get some ideas of what primitives the Python &lt;code&gt;threading&lt;/code&gt; module provides.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;re going to imagine a program that needs to read messages from a network and write them to disk. The program does not request a message when it wants. It must be listening and accept messages as they come in. The messages will not come in at a regular pace, but will be coming in bursts. This part of the program is called the producer.&lt;/p&gt;
&lt;p&gt;On the other side, once you have a message, you need to write it to a database. The database access is slow, but fast enough to keep up to the average pace of messages. It is &lt;em&gt;not&lt;/em&gt; fast enough to keep up when a burst of messages comes in. This part is the consumer.&lt;/p&gt;
&lt;p&gt;In between the producer and the consumer, you will create a &lt;code&gt;Pipeline&lt;/code&gt; that will be the part that changes as you learn about different synchronization objects.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the basic layout. Let&amp;rsquo;s look at a solution using &lt;code&gt;Lock&lt;/code&gt;. It doesn&amp;rsquo;t work perfectly, but it uses tools you already know, so it&amp;rsquo;s a good place to start.&lt;/p&gt;
&lt;h3 id=&quot;producer-consumer-using-lock&quot;&gt;Producer-Consumer Using &lt;code&gt;Lock&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Since this is an article about Python &lt;code&gt;threading&lt;/code&gt;, and since you just read about the &lt;code&gt;Lock&lt;/code&gt; primitive, let&amp;rsquo;s try to solve this problem with two threads using a &lt;code&gt;Lock&lt;/code&gt; or two.&lt;/p&gt;
&lt;p&gt;The general design is that there is a &lt;code&gt;producer&lt;/code&gt; thread that reads from the fake network and puts the message into a &lt;code&gt;Pipeline&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a message from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Send a sentinel message to tell consumer we&amp;#39;re done&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To generate a fake message, the &lt;code&gt;producer&lt;/code&gt; gets a random number between one and one hundred. It calls &lt;code&gt;.set_message()&lt;/code&gt; on the &lt;code&gt;pipeline&lt;/code&gt; to send it to the &lt;code&gt;consumer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;producer&lt;/code&gt; also  uses a &lt;code&gt;SENTINEL&lt;/code&gt; value to signal the consumer to stop after it has sent ten values. This is a little awkward, but don&amp;rsquo;t worry, you&amp;rsquo;ll see ways to get rid of this &lt;code&gt;SENTINEL&lt;/code&gt; value after you work through this example.&lt;/p&gt;
&lt;p&gt;On the other side of the &lt;code&gt;pipeline&lt;/code&gt; is the consumer:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SENTINEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; reads a message from the &lt;code&gt;pipeline&lt;/code&gt; and writes it to a fake database, which in this case is just printing it to the display. If it gets the &lt;code&gt;SENTINEL&lt;/code&gt; value, it returns from the function, which will terminate the thread.&lt;/p&gt;
&lt;p&gt;Before you look at the really interesting part, the &lt;code&gt;Pipeline&lt;/code&gt;, here&amp;rsquo;s the &lt;code&gt;__main__&lt;/code&gt; section, which spawns these threads:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This should look fairly familiar as it&amp;rsquo;s close to the &lt;code&gt;__main__&lt;/code&gt; code in the previous examples.&lt;/p&gt;
&lt;p&gt;Remember that you can turn on &lt;code&gt;DEBUG&lt;/code&gt; logging to see all of the logging messages by uncommenting  this line:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It can be worthwhile to walk through the &lt;code&gt;DEBUG&lt;/code&gt; logging messages to see exactly where each thread acquires and releases the locks.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s take a look at the &lt;code&gt;Pipeline&lt;/code&gt; that passes messages from the &lt;code&gt;producer&lt;/code&gt; to the &lt;code&gt;consumer&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Class to allow a single element pipeline between producer and consumer.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to acquire getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:have getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to release setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:setlock released&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to acquire setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:have setlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to release getlock&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:getlock released&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Woah! That&amp;rsquo;s a lot of code. A pretty high percentage of that is just logging statements to make it easier to see what&amp;rsquo;s happening when you run it. Here&amp;rsquo;s the same code with all of the logging statements removed:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Class to allow a single element pipeline between producer and consumer.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;coNsumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acquire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer_lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That seems a bit more manageable. The &lt;code&gt;Pipeline&lt;/code&gt; in this version of your code has three members:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.message&lt;/code&gt;&lt;/strong&gt; stores the message to pass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.producer_lock&lt;/code&gt;&lt;/strong&gt; is a &lt;code&gt;threading.Lock&lt;/code&gt; object that restricts access to the message by the &lt;code&gt;producer&lt;/code&gt; thread.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;.consumer_lock&lt;/code&gt;&lt;/strong&gt; is also a &lt;code&gt;threading.Lock&lt;/code&gt; that restricts access to the message by the &lt;code&gt;consumer&lt;/code&gt; thread.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;__init__()&lt;/code&gt; initializes these three members and then calls &lt;code&gt;.acquire()&lt;/code&gt; on the &lt;code&gt;.consumer_lock&lt;/code&gt;. This is the state you want to start in. The &lt;code&gt;producer&lt;/code&gt; is allowed to add a new message, but the &lt;code&gt;consumer&lt;/code&gt; needs to wait until a message is present.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.get_message()&lt;/code&gt; and &lt;code&gt;.set_messages()&lt;/code&gt; are nearly opposites. &lt;code&gt;.get_message()&lt;/code&gt; calls &lt;code&gt;.acquire()&lt;/code&gt; on the &lt;code&gt;consumer_lock&lt;/code&gt;. This is the call that will make the &lt;code&gt;consumer&lt;/code&gt; wait until a message is ready.&lt;/p&gt;
&lt;p&gt;Once the &lt;code&gt;consumer&lt;/code&gt; has acquired the &lt;code&gt;.consumer_lock&lt;/code&gt;, it copies out the value in &lt;code&gt;.message&lt;/code&gt; and then calls &lt;code&gt;.release()&lt;/code&gt; on the &lt;code&gt;.producer_lock&lt;/code&gt;. Releasing this lock is what allows the &lt;code&gt;producer&lt;/code&gt; to insert the next message into the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before you go on to &lt;code&gt;.set_message()&lt;/code&gt;, there&amp;rsquo;s something subtle going on in &lt;code&gt;.get_message()&lt;/code&gt; that&amp;rsquo;s pretty easy to miss. It might seem tempting to get rid of &lt;code&gt;message&lt;/code&gt; and just have the function end with &lt;code&gt;return self.message&lt;/code&gt;. See if you can figure out why you don&amp;rsquo;t want to do that before moving on.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the answer. As soon as the &lt;code&gt;consumer&lt;/code&gt; calls &lt;code&gt;.producer_lock.release()&lt;/code&gt;, it can be swapped out, and the &lt;code&gt;producer&lt;/code&gt; can start running. That could happen before &lt;code&gt;.release()&lt;/code&gt; returns! This means that there is a slight possibility that when the function returns &lt;code&gt;self.message&lt;/code&gt;, that could actually be the &lt;em&gt;next&lt;/em&gt; message generated, so you would lose the first message. This is another example of a race condition.&lt;/p&gt;
&lt;p&gt;Moving on to &lt;code&gt;.set_message()&lt;/code&gt;, you can see the opposite side of the transaction. The &lt;code&gt;producer&lt;/code&gt; will call this with a message. It will acquire the &lt;code&gt;.producer_lock&lt;/code&gt;, set the &lt;code&gt;.message&lt;/code&gt;, and the call &lt;code&gt;.release()&lt;/code&gt; on then &lt;code&gt;consumer_lock&lt;/code&gt;, which will allow the &lt;code&gt;consumer&lt;/code&gt; to read that value.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s run the code that has logging set to &lt;code&gt;WARNING&lt;/code&gt; and see what it looks like:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./prodcom_lock.py
&lt;span class=&quot;go&quot;&gt;Producer got data 43&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 43&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 86&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 45&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 40&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 86&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 62&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 40&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 62&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 16&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 61&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 16&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 73&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 61&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got data 22&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 73&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing data: 22&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At first, you might find it odd that the producer gets two messages before the consumer even runs. If you look back at the &lt;code&gt;producer&lt;/code&gt; and &lt;code&gt;.set_message()&lt;/code&gt;, you will notice that the only place it will wait for a &lt;code&gt;Lock&lt;/code&gt; is when it attempts to put the message into the pipeline. This is done after the &lt;code&gt;producer&lt;/code&gt; gets the message and logs that it has it.&lt;/p&gt;
&lt;p&gt;When the &lt;code&gt;producer&lt;/code&gt; attempts to send this second message, it will call &lt;code&gt;.set_message()&lt;/code&gt; the second time and it will block.&lt;/p&gt;
&lt;p&gt;The operating system can swap threads at any time, but it generally lets each thread have a reasonable amount of time to run before swapping it out. That&amp;rsquo;s why the &lt;code&gt;producer&lt;/code&gt; usually runs until it blocks in the second call to &lt;code&gt;.set_message()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once a thread is blocked, however, the operating system will always swap it out and find a different thread to run. In this case, the only other thread with anything to do is the &lt;code&gt;consumer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; calls &lt;code&gt;.get_message()&lt;/code&gt;, which reads the message and calls &lt;code&gt;.release()&lt;/code&gt; on the &lt;code&gt;.producer_lock&lt;/code&gt;, thus allowing the &lt;code&gt;producer&lt;/code&gt; to run again the next time threads are swapped.&lt;/p&gt;
&lt;p&gt;Notice that the first message was &lt;code&gt;43&lt;/code&gt;, and that is exactly what the &lt;code&gt;consumer&lt;/code&gt; read, even though the &lt;code&gt;producer&lt;/code&gt; had already generated the &lt;code&gt;45&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;While it works for this limited test, it is not a great solution to the producer-consumer problem in general because it only allows a single value in the pipeline at a time. When the &lt;code&gt;producer&lt;/code&gt; gets a burst of messages, it will have nowhere to put them.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s move on to a better way to solve this problem, using a &lt;code&gt;Queue&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;producer-consumer-using-queue&quot;&gt;Producer-Consumer Using &lt;code&gt;Queue&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you want to be able to handle more than one value in the pipeline at a time, you&amp;rsquo;ll need a data structure for the pipeline that allows the number to grow and shrink as data backs up from the &lt;code&gt;producer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Python&amp;rsquo;s standard library has a &lt;code&gt;queue&lt;/code&gt; module which, in turn, has a &lt;code&gt;Queue&lt;/code&gt; class. Let&amp;rsquo;s change the &lt;code&gt;Pipeline&lt;/code&gt; to use a &lt;code&gt;Queue&lt;/code&gt; instead of just a variable protected by a &lt;code&gt;Lock&lt;/code&gt;. You&amp;rsquo;ll also use a different way to stop the worker threads by using a different primitive from Python &lt;code&gt;threading&lt;/code&gt;, an &lt;code&gt;Event&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the &lt;code&gt;Event&lt;/code&gt;. The &lt;code&gt;threading.Event&lt;/code&gt; object allows one thread to signal an &lt;code&gt;event&lt;/code&gt; while many other threads can be waiting for that &lt;code&gt;event&lt;/code&gt; to happen. The key usage in this code is that the threads that are waiting for the event do not necessarily need to stop what they are doing, they can just check the status of the &lt;code&gt;Event&lt;/code&gt; every once in a while.&lt;/p&gt;
&lt;p&gt;The triggering of the event can be many things. In this example, the main thread will simply sleep for a while and then &lt;code&gt;.set()&lt;/code&gt; it:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# logging.getLogger().setLevel(logging.DEBUG)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main: about to set event&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;15 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only changes here are the creation of the &lt;code&gt;event&lt;/code&gt; object on line 6, passing the &lt;code&gt;event&lt;/code&gt; as a parameter on lines 8 and 9, and the final section on lines 11 to 13, which sleep for a second, log a message, and then call &lt;code&gt;.set()&lt;/code&gt; on the event.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;producer&lt;/code&gt; also did not have to change too much:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a number from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer received EXIT event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It now will loop until it sees that the event was set on line 3. It also no longer puts the &lt;code&gt;SENTINEL&lt;/code&gt; value into the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;consumer&lt;/code&gt; had to change a little more:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;            &lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  (queue size=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer received EXIT event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While you got to take out the code related to the &lt;code&gt;SENTINEL&lt;/code&gt; value, you did have to do a slightly more complicated &lt;code&gt;while&lt;/code&gt; condition. Not only does it loop until the &lt;code&gt;event&lt;/code&gt; is set, but it also needs to keep looping until the &lt;code&gt;pipeline&lt;/code&gt; has been emptied.&lt;/p&gt;
&lt;p&gt;Making sure the queue is empty before the consumer finishes prevents another fun issue. If the &lt;code&gt;consumer&lt;/code&gt; does exit while the &lt;code&gt;pipeline&lt;/code&gt; has messages in it, there are two bad things that can happen. The first is that you lose those final messages, but the more serious one is that the &lt;code&gt;producer&lt;/code&gt; can get caught on the &lt;code&gt;producer_lock&lt;/code&gt; and never return.&lt;/p&gt;
&lt;p&gt;This happens if the &lt;code&gt;event&lt;/code&gt; gets triggered after the &lt;code&gt;producer&lt;/code&gt; has checked the &lt;code&gt;.is_set()&lt;/code&gt; condition but before it calls &lt;code&gt;pipeline.set_message()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If that happens, it&amp;rsquo;s possible for the producer to wake up and exit with the &lt;code&gt;.producer_lock&lt;/code&gt; still being held. The &lt;code&gt;producer&lt;/code&gt; will then try to &lt;code&gt;.acquire()&lt;/code&gt; the &lt;code&gt;.producer_lock&lt;/code&gt;, but the &lt;code&gt;consumer&lt;/code&gt; has exited and will never &lt;code&gt;.release()&lt;/code&gt; it.&lt;/p&gt;
&lt;p&gt;The rest of the &lt;code&gt;consumer&lt;/code&gt; should look familiar.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Pipeline&lt;/code&gt; has changed dramatically, however:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to get from queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:got &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; from queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;10 &lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;11 &lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;12 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:about to add &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;13 &lt;/span&gt;        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt;14 &lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:added &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to queue&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that &lt;code&gt;Pipeline&lt;/code&gt; is a subclass of &lt;code&gt;queue.Queue&lt;/code&gt;. &lt;code&gt;Queue&lt;/code&gt; has an optional parameter when initializing to specify a maximum size of the queue.&lt;/p&gt;
&lt;p&gt;If you give a positive number for &lt;code&gt;maxsize&lt;/code&gt;, it will limit the queue to that number of elements, causing &lt;code&gt;.put()&lt;/code&gt; to block until there are fewer than &lt;code&gt;maxsize&lt;/code&gt; elements. If you don&amp;rsquo;t specify &lt;code&gt;maxsize&lt;/code&gt;, then the queue will grow to the limits of your computer&amp;rsquo;s memory.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.get_message()&lt;/code&gt; and &lt;code&gt;.set_message()&lt;/code&gt; got much smaller. They basically wrap &lt;code&gt;.get()&lt;/code&gt; and &lt;code&gt;.put()&lt;/code&gt; on the &lt;code&gt;Queue&lt;/code&gt;. You might be wondering where all of the locking code that prevents the threads from causing race conditions went.&lt;/p&gt;
&lt;p&gt;The core devs who wrote the standard library knew that a &lt;code&gt;Queue&lt;/code&gt; is frequently used in multi-threading environments and incorporated all of that locking code inside the &lt;code&gt;Queue&lt;/code&gt; itself. &lt;code&gt;Queue&lt;/code&gt; is thread-safe.&lt;/p&gt;
&lt;p&gt;Running this program looks like the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ./prodcom_queue.py
&lt;span class=&quot;go&quot;&gt;Producer got message: 32&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 51&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 25&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 94&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 29&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 32 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 96&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 51 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 25 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 31&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;[many lines deleted]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Producer got message: 80&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 94 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 33&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 20 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 48&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 31 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 52&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 98 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Main: about to set event&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer got message: 13&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 59 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Producer received EXIT event. Exiting&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 75 (queue size=6)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 97 (queue size=5)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 80 (queue size=4)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 33 (queue size=3)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 48 (queue size=2)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 52 (queue size=1)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer storing message: 13 (queue size=0)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Consumer received EXIT event. Exiting&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you read through the output in my example, you can see some interesting things happening. Right at the top, you can see the &lt;code&gt;producer&lt;/code&gt; got to create five messages and place four of them on the queue. It got swapped out by the operating system before it could place the fifth one.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;consumer&lt;/code&gt; then ran and pulled off the first message. It printed out that message as well as how deep the queue was at that point:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Consumer storing message: 32 (queue size=3)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is how you know that the fifth message hasn&amp;rsquo;t made it into the &lt;code&gt;pipeline&lt;/code&gt; yet. The queue is down to size three after a single message was removed. You also know that the &lt;code&gt;queue&lt;/code&gt; can hold ten messages, so the &lt;code&gt;producer&lt;/code&gt; thread didn&amp;rsquo;t get blocked by the &lt;code&gt;queue&lt;/code&gt;. It was swapped out by the OS.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your output will be different. Your output will change from run to run. That&amp;rsquo;s the fun part of working with threads!&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As the program starts to wrap up, can you see the main thread generating the &lt;code&gt;event&lt;/code&gt; which causes the &lt;code&gt;producer&lt;/code&gt; to exit immediately. The &lt;code&gt;consumer&lt;/code&gt; still has a bunch of work do to, so it keeps running until it has cleaned out the &lt;code&gt;pipeline&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Try playing with different queue sizes and calls to &lt;code&gt;time.sleep()&lt;/code&gt; in the &lt;code&gt;producer&lt;/code&gt; or the &lt;code&gt;consumer&lt;/code&gt; to simulate longer network or disk access times respectively. Even slight changes to these elements of the program will make large differences in your results.&lt;/p&gt;
&lt;p&gt;This is a much better solution to the producer-consumer problem, but you can simplify it even more. The &lt;code&gt;Pipeline&lt;/code&gt; really isn&amp;rsquo;t needed for this problem. Once you take away the logging, it just becomes a &lt;code&gt;queue.Queue&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what the final code looks like using &lt;code&gt;queue.Queue&lt;/code&gt; directly:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;concurrent.futures&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;logging&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;queue&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;threading&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re getting a number from the network.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;101&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer got message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Producer received event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Pretend we&amp;#39;re saving a number in the database.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer storing message: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; (size=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%d&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qsize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Consumer received event. Exiting&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(asctime)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%(message)s&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basicConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;datefmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;maxsize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concurrent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;futures&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;producer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;consumer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Main: about to set event&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s easier to read and shows how using Python&amp;rsquo;s built-in primitives can simplify a complex problem.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Lock&lt;/code&gt; and &lt;code&gt;Queue&lt;/code&gt; are handy classes to solve concurrency issues, but there are others provided by the standard library. Before you wrap up this tutorial, let&amp;rsquo;s do a quick survey of some of them.&lt;/p&gt;
&lt;h2 id=&quot;threading-objects&quot;&gt;Threading Objects&lt;/h2&gt;
&lt;p&gt;There are a few more primitives offered by the Python &lt;code&gt;threading&lt;/code&gt; module. While you didn&amp;rsquo;t need these for the examples above, they can come in handy in different use cases, so it&amp;rsquo;s good to be familiar with them.&lt;/p&gt;
&lt;h3 id=&quot;semaphore&quot;&gt;Semaphore&lt;/h3&gt;
&lt;p&gt;The first Python &lt;code&gt;threading&lt;/code&gt; object to look at is &lt;code&gt;threading.Semaphore&lt;/code&gt;. A &lt;code&gt;Semaphore&lt;/code&gt; is a counter with a few special properties. The first one is that the counting is atomic. This means that there is a guarantee that the operating system will not swap out the thread in the middle of incrementing or decrementing the counter.&lt;/p&gt;
&lt;p&gt;The internal counter is incremented when you call &lt;code&gt;.release()&lt;/code&gt; and decremented when you call &lt;code&gt;.acquire()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The next special property is that if a thread calls &lt;code&gt;.acquire()&lt;/code&gt; when the counter is zero, that thread will block until a different thread calls &lt;code&gt;.release()&lt;/code&gt; and increments the counter to one.&lt;/p&gt;
&lt;p&gt;Semaphores are frequently used to protect a resource that has a limited capacity. An example would be if you have a pool of connections and want to limit the size of that pool to a specific number.&lt;/p&gt;
&lt;h3 id=&quot;timer&quot;&gt;Timer&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;threading.Timer&lt;/code&gt; is a way to schedule a function to be called after a certain amount of time has passed. You create a &lt;code&gt;Timer&lt;/code&gt; by passing in a number of seconds to wait and a function to call:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;threading&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;30.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;my_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You start the &lt;code&gt;Timer&lt;/code&gt; by calling &lt;code&gt;.start()&lt;/code&gt;. The function will be called on a new thread at some point after the specified time, but be aware that there is no promise that it will be called exactly at the time you want.&lt;/p&gt;
&lt;p&gt;If you want to stop a &lt;code&gt;Timer&lt;/code&gt; that you&amp;rsquo;ve already started, you can cancel it by calling &lt;code&gt;.cancel()&lt;/code&gt;. Calling &lt;code&gt;.cancel()&lt;/code&gt; after the &lt;code&gt;Timer&lt;/code&gt; has triggered does nothing and does not produce an exception.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Timer&lt;/code&gt; can be used to prompt a user for action after a specific amount of time. If the user does the action before the &lt;code&gt;Timer&lt;/code&gt; expires, &lt;code&gt;.cancel()&lt;/code&gt; can be called.&lt;/p&gt;
&lt;h3 id=&quot;barrier&quot;&gt;Barrier&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;threading.Barrier&lt;/code&gt; can be used to keep a fixed number of threads in sync. When creating a &lt;code&gt;Barrier&lt;/code&gt;, the caller must specify how many threads will be synchronizing on it. Each thread calls &lt;code&gt;.wait()&lt;/code&gt; on the &lt;code&gt;Barrier&lt;/code&gt;. They all will remain blocked until the specified number of threads are waiting, and then the are all released at the same time.&lt;/p&gt;
&lt;p&gt;Remember that threads are scheduled by the operating system so, even though all of the threads are released simultaneously, they will be scheduled to run one at a time.&lt;/p&gt;
&lt;p&gt;One use for a &lt;code&gt;Barrier&lt;/code&gt; is to allow a pool of threads to initialize themselves. Having the threads wait on a &lt;code&gt;Barrier&lt;/code&gt; after they are initialized will ensure that none of the threads start running before all of the threads are finished with their initialization.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-threading-in-python&quot;&gt;Conclusion: Threading in Python&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve now seen much of what Python &lt;code&gt;threading&lt;/code&gt; has to offer and some examples of how to build threaded programs and the problems they solve. You&amp;rsquo;ve also seen a few instances of the problems that arise when writing and debugging threaded programs.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to explore other options for concurrency in Python, check out &lt;a href=&quot;https://realpython.com/python-concurrency/&quot;&gt;Speed Up Your Python Program With Concurrency&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in doing a deep dive on the &lt;code&gt;asyncio&lt;/code&gt; module, go read &lt;a href=&quot;https://realpython.com/async-io-python/&quot;&gt;Async IO in Python: A Complete Walkthrough&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Whatever you do, you now have the information and confidence you need to write programs using Python threading!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to reader JL Diaz for helping to clean up the introduction.&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;&lt;i class=&quot;fa fa-graduation-cap&quot; aria-hidden=&quot;true&quot;&gt;&lt;/i&gt; Take the Quiz:&lt;/strong&gt; Test your knowledge with our interactive “Python Threading” quiz. Upon completion you will receive a score so you can track your learning progress over time:&lt;/p&gt;&lt;p class=&quot;text-center my-2&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;/quizzes/python-threading/&quot; target=&quot;_blank&quot;&gt;Take the Quiz »&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Intro to Object-Oriented Programming (OOP) in Python</title>
      <id>https://realpython.com/courses/intro-object-oriented-programming-oop-python/</id>
      <link href="https://realpython.com/courses/intro-object-oriented-programming-oop-python/"/>
      <updated>2019-03-21T14:00:00+00:00</updated>
      <summary>In this video series, you&#39;ll learn the fundamentals of object-oriented programming (OOP) in Python and how to work with classes, objects, and constructors.</summary>
      <content type="html">
        &lt;p&gt;Object-oriented programming is one of the biggest and most important subjects in all of programming. This series will provide you with a basic conceptual understanding of Object-Oriented Programming so you can take your Python programming skills to the next level.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll learn how to define custom types using classes, and how to instantiate those classes into python objects that can be used throughout your program.&lt;/p&gt;
&lt;p&gt;Finally, you&amp;rsquo;ll learn how classes can subclass one another with  brief introduction to inheritance, which allows you to write maintainable and less redundant Python code.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>13 Project Ideas for Intermediate Python Developers</title>
      <id>https://realpython.com/intermediate-python-project-ideas/</id>
      <link href="https://realpython.com/intermediate-python-project-ideas/"/>
      <updated>2019-03-20T14:00:00+00:00</updated>
      <summary>In this article, you&#39;ll learn how you can get started on 13 Python project ideas that are just right for intermediate Python developers. They&#39;ll challenge you enough to help you become a better Pythonista but will still be doable!</summary>
      <content type="html">
        &lt;p&gt;Learning the basics of Python is a wonderful experience. But the euphoria of just learning can be replaced by the hunger for hands-on projects. It&amp;rsquo;s normal to want to build projects, hence the need for project ideas.&lt;/p&gt;
&lt;p&gt;The problem though is that some projects are either too simple for an intermediate Python developer or too hard. This article will suggest projects you can work on as an intermediate Python developer. These project ideas will provide the appropriate level of challenge for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The importance of building projects&lt;/li&gt;
&lt;li&gt;The major platforms you can build projects for&lt;/li&gt;
&lt;li&gt;Thirteen project ideas you can work on&lt;/li&gt;
&lt;li&gt;Some tips for working on projects&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-importance-of-building-projects&quot;&gt;The Importance of Building Projects&lt;/h2&gt;
&lt;p&gt;Working on projects is vital to pushing your career as a Python developer forward. They make you apply the skills and knowledge you&amp;rsquo;re acquiring.&lt;/p&gt;
&lt;p&gt;Projects can help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build confidence&lt;/strong&gt;: You will believe more in your ability to create software regardless the level of complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explore other technologies&lt;/strong&gt;: You will learn about other technologies needed in building a complete product such as databases, servers, and other languages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Understand programming concepts better&lt;/strong&gt;: You will learn to write code better and understand concepts such as design patterns and object-oriented programming.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Experience a complete software development life cycle&lt;/strong&gt;: You will learn how to plan before writing code, manage the coding process and update software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is a lot to gain from building projects as a Python developer.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-project-platform&quot;&gt;Choosing a Project Platform&lt;/h2&gt;
&lt;p&gt;You need to build your software to run on a platform so that people who lack certain technical knowledge can use your software. The web, desktop, and command-line are the three major platforms you&amp;rsquo;ll want to build your projects for.&lt;/p&gt;
&lt;h3 id=&quot;web&quot;&gt;Web&lt;/h3&gt;
&lt;p&gt;Web applications are applications that run on the web, they can be accessed on any device without being downloaded, provided there is access to the internet. If you want your projects to be accessible by everyone with internet access, it needs to be a web application. &lt;/p&gt;
&lt;p&gt;A web application has a back end and front end. The back end is the part where the business logic is: your back-end code will manipulate and store data. The front end is the interface of the application: your front-end code will determine the look of a web application.&lt;/p&gt;
&lt;p&gt;As an intermediate Python developer, your major focus will be the back-end code. However, the front-end code is important too, so you will need some knowledge of HTML, CSS, and maybe JavaScript to create a simple-looking interface. Just the basics will be enough.&lt;/p&gt;
&lt;p&gt;Another option is to use Python for both the front end and back end. Thanks to the &lt;a href=&quot;https://anvil.works/learn&quot;&gt;&lt;code&gt;anvil&lt;/code&gt;&lt;/a&gt; library, which eliminates the need for HTML, CSS, and JavaScript, you can focus on Python code alone.&lt;/p&gt;
&lt;p&gt;You can build web applications with Python through web frameworks such as &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;&lt;code&gt;django&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;&lt;code&gt;flask&lt;/code&gt;&lt;/a&gt;. The list of frameworks for building web applications using Python is long. There are plenty to choose from, but &lt;code&gt;django&lt;/code&gt; and &lt;code&gt;flask&lt;/code&gt; remain the most popular web frameworks.&lt;/p&gt;
&lt;h3 id=&quot;desktop-gui&quot;&gt;Desktop GUI&lt;/h3&gt;
&lt;p&gt;Every time you perform a task on your PC, be it a desktop or laptop, it is through an application. As an intermediate Python developer, you can make your own desktop applications. &lt;/p&gt;
&lt;p&gt;You do not have to learn any front-end technology to create your own Graphical User Interface (GUI) applications, as you saw with web applications. You can build all the parts using Python.&lt;/p&gt;
&lt;p&gt;There are frameworks for building your desktop applications. &lt;a href=&quot;https://pysimplegui.readthedocs.io/tutorial/&quot;&gt;&lt;code&gt;PySimpleGUI&lt;/code&gt;&lt;/a&gt; is one of them, and it&amp;rsquo;s pretty user-friendly for an intermediate Python developer. &lt;/p&gt;
&lt;p&gt;An advanced GUI framework like &lt;a href=&quot;https://pypi.org/project/PyQt5/&quot;&gt;&lt;code&gt;PyQt5&lt;/code&gt;&lt;/a&gt; is quite powerful, but it may have a steep learning curve.&lt;/p&gt;
&lt;p&gt;The software you create for the Desktop GUI is able to work on any of the Windows, Linux, or Mac operating systems. All you have to do after creating the project is compile it to an executable for your operating system of choice.&lt;/p&gt;
&lt;h3 id=&quot;command-line&quot;&gt;Command-Line&lt;/h3&gt;
&lt;p&gt;Command-line applications are those applications that work in a console window. This is the command prompt on Windows and the Terminal on Linux and Mac. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d click to use a web or GUI application, but you&amp;rsquo;d type in commands for command-line applications. Users of command-line applications need to have some technical knowledge since they&amp;rsquo;ll need to use commands.&lt;/p&gt;
&lt;p&gt;Command-line applications may not be as beautiful or easy to use as web or GUI applications, but that doesn&amp;rsquo;t make them less powerful than web or GUI applications.&lt;/p&gt;
&lt;p&gt;You can improve the look of your command-line applications by applying colors to the text. There are libraries you can use for coloring, such as &lt;a href=&quot;https://pypi.org/project/colorama/&quot;&gt;&lt;code&gt;colorama&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/colored/&quot;&gt;&lt;code&gt;colored&lt;/code&gt;&lt;/a&gt;. You can spice things up and use some color.&lt;/p&gt;
&lt;p&gt;You can use frameworks such as &lt;a href=&quot;https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/&quot;&gt;&lt;code&gt;docopt&lt;/code&gt;, &lt;code&gt;argparse&lt;/code&gt;, and &lt;code&gt;click&lt;/code&gt;&lt;/a&gt; to build your applications.&lt;/p&gt;
&lt;h2 id=&quot;web-project-ideas&quot;&gt;Web Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for the web. These project ideas can be classified as utility and education tools. &lt;/p&gt;
&lt;p&gt;Here are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content Aggregator&lt;/li&gt;
&lt;li&gt;Regex Query Tool&lt;/li&gt;
&lt;li&gt;URL Shortener&lt;/li&gt;
&lt;li&gt;Post-It Note&lt;/li&gt;
&lt;li&gt;Quiz Application&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;content-aggregator&quot;&gt;Content Aggregator&lt;/h3&gt;
&lt;p&gt;Content is king. It exists everywhere on the web, from blogs to social media platforms. To keep up, you need to search for new information on the internet constantly. One way to stay updated is to check all the sites manually to see what the new posts are. But this is time consuming and quite tiring.&lt;/p&gt;
&lt;p&gt;This is where the content aggregator comes in: A content aggregator fetches information from various places online and gathers all of that information in one place. Therefore, you don&amp;rsquo;t have to visit multiple sites to get the latest info: one website is enough.&lt;/p&gt;
&lt;p&gt;With the content aggregator, all of the latest information can be gotten from one site that aggregates all the content. People can see the posts that interest them and can decide to find out more about them without traipsing all over the internet. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Content Aggregators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Content Aggregator idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://alltop.com/&quot;&gt;AllTop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.hvper.com/&quot;&gt;Hvper&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to aggregate content. First, you need to know what sites you&amp;rsquo;ll want the Content Aggregator to get content from. Then, you can use libraries such as &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;&lt;code&gt;requests&lt;/code&gt;&lt;/a&gt; for sending HTTP requests and &lt;a href=&quot;https://www.crummy.com/software/BeautifulSoup/bs4/doc/&quot;&gt;&lt;code&gt;BeautifulSoup&lt;/code&gt;&lt;/a&gt; to parse and scrape the necessary content from the sites.&lt;/p&gt;
&lt;p&gt;Your application can implement its content aggregation as a background process. Libraries such as &lt;code&gt;celery&lt;/code&gt; or &lt;code&gt;apscheduler&lt;/code&gt; can help with that. You can try out &lt;code&gt;apscheduler&lt;/code&gt;. It&amp;rsquo;s great for small background processes.&lt;/p&gt;
&lt;p&gt;After scraping content from various sites, you&amp;rsquo;ll need to save it somewhere. So, you&amp;rsquo;ll use a database to save the scraped content.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a tougher challenge, you can add more websites. This will help you &lt;a href=&quot;https://realpython.com/python-web-scraping-practical-introduction/&quot;&gt;learn how to study and extract information from websites&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also have users subscribe to certain sites that you aggregate. Then, at the end of the day, the content aggregator will send the articles for that day to the email address of the user.&lt;/p&gt;
&lt;h3 id=&quot;regex-query-tool&quot;&gt;Regex Query Tool&lt;/h3&gt;
&lt;p&gt;You and I deal with text daily. This article, which is also text, has a structure. This makes it easier for you to understand. Sometimes, you need to find certain information in text, and using the regular search tool in text editors can be ineffective. &lt;/p&gt;
&lt;p&gt;This is where the Regex Query Tool comes in. A regex is a set of strings, so the regex query tool will check for the validity of the queries. When the regex matches patterns in the text, it tells the user and highlights the matched patterns. So, your Regex Query Tool will check the validity of the regex strings passed in by the user.&lt;/p&gt;
&lt;p&gt;With the Regex Query Tool, users can quickly check the validity of their regex strings on the web. This makes it easier for them, instead of having to check the strings with a text editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Regex Query Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Regex Query Tool idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freeformatter.com/regex-tester.html&quot;&gt;FreeFormatter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.regextester.com/&quot;&gt;RegexTester&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this type of project is to tell the user the validity of the inputted query strings. You can make it give a positive or negative response such as &lt;code&gt;Query String Is Valid&lt;/code&gt; and &lt;code&gt;Query String Is Invalid&lt;/code&gt;, implementing the positive response in green and the negative in red.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t have to implement the query tool from scratch. You can use Python&amp;rsquo;s standard &lt;code&gt;re&lt;/code&gt; library, which you can use to run the query strings on the inputted text. The &lt;code&gt;re&lt;/code&gt; library will return &lt;code&gt;None&lt;/code&gt; when the query string matches nothing, and it&amp;rsquo;ll return the matched strings when positive.&lt;/p&gt;
&lt;p&gt;Some users may not understand regex fully, so you can make a page to explain how regex works. You can make documentation that is interesting enough to keep the users excited about learning and understanding regex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Making a project that just returns the validity of the regex is fine. But you can also add a replacement feature. This means the application will check for the validity of the regex and also allow users to replace the matched strings with something else. So the tool is no longer a find tool but also a replace tool.&lt;/p&gt;
&lt;h3 id=&quot;url-shortener&quot;&gt;URL Shortener&lt;/h3&gt;
&lt;p&gt;URLs can be extremely long and not user-friendly. When people share links or even try to remember a URL, it&amp;rsquo;s difficult because most URLs are filled with more difficult characters and don&amp;rsquo;t form meaningful words.&lt;/p&gt;
&lt;p&gt;This is where the URL Shortener comes in. A URL Shortener reduces the characters or letters in a URL, making them easier to read and remember. A URL like &lt;code&gt;xyz.com/wwryb78&amp;amp;svnhkn%sghq?sfiyh&lt;/code&gt; can be shortened to &lt;code&gt;xyz.com/piojwr&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With the URL Shortener, URLs become a joy to work with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of URL Shorteners&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the URL Shortener idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://bitly.com/&quot;&gt;Bitly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.msht.us/&quot;&gt;MeShort&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to shorten URLs. The main task the application will accomplish is to shorten URLs and then redirect users to the original URL when the shortened URL is visited.&lt;/p&gt;
&lt;p&gt;In the application, the users will input the original URL, and they will get the new, shortened URL as the result. To do this, you can use a combination of the &lt;code&gt;random&lt;/code&gt; and &lt;code&gt;string&lt;/code&gt; modules to generate the characters for the shortened URL.&lt;/p&gt;
&lt;p&gt;Since users will visit the shortened URL days, months, or even years after, you&amp;rsquo;ll need to save the original and shortened URLs in a database. When a request comes in, the application checks if the URL exists and redirects to the original, or else it redirects to a 404 page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Generating a shortened URL with random characters makes for a better URL than the long, random ones. But, you can make the result better for the users. You can add a feature to customize URLs, so the users can customize the generated URLs themselves. &lt;/p&gt;
&lt;p&gt;Without a doubt, a custom &lt;code&gt;xyz.com/mysite&lt;/code&gt; URL is better than a randomly generated &lt;code&gt;xyz.com/piojwr&lt;/code&gt; URL.&lt;/p&gt;
&lt;h3 id=&quot;post-it-note&quot;&gt;Post-It Note&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s human to have many thoughts and ideas in a day, but it&amp;rsquo;s also human to forget. One way to work around forgetting things is to jot them down before they disappear into thin air. While some of forgotten thoughts and ideas may be trivial, some can be quite powerful.&lt;/p&gt;
&lt;p&gt;This is where a Post-It note comes in: A Post-It note is a small paper with low-tack adhesive at the back, making it attachable to surfaces such as documents, walls. Post-It notes make it easier to jot things down. The Post-It note project idea is something similar. It allows users to jot things down, making them accessible anywhere, since it&amp;rsquo;s a web application.&lt;/p&gt;
&lt;p&gt;With the Post-It note, people can now jot things down anywhere, without the fear of forgetting things or misplacing the notes&amp;mdash;which is a possibility with physical notes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Post-It Notes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Post-It Note idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pinup.com/&quot;&gt;Pinup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://note.ly/&quot;&gt;Note.ly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to allow users to jot down thoughts. This means that each user will have their own notes, so the application will need to have an account creation feature. This ensures that the notes of each user remain private to them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;django&lt;/code&gt; comes with a user authentication system, so it may be a good choice. You can use other frameworks like &lt;code&gt;bottle&lt;/code&gt; or &lt;code&gt;flask&lt;/code&gt;, but you&amp;rsquo;ll have to implement the user authentication system on your own.&lt;/p&gt;
&lt;p&gt;Since users may need to separate their notes under different sections, implementing a feature to allow users to categorize their notes will make the application more useful. &lt;/p&gt;
&lt;p&gt;As an example, you may need to have notes on algorithms and data structures, so you&amp;rsquo;ll need to be able to separate the notes in those categories.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to store the information and notes of each user, so a database becomes an essential part of this project. The &lt;code&gt;MySQLdb&lt;/code&gt; module can be used if you want to use a MySQL database or the &lt;code&gt;psycopg2&lt;/code&gt; module for a PostgreSQL database. There are other modules you can use, but it all depends on the database you choose to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s human for users to forget their ideas, it&amp;rsquo;s also human for them to forget that they even made a note somewhere. You can add a feature to remind users of their notes. This feature will allow users to set a time for the reminder, so the application will send the reminder to the users when it&amp;rsquo;s time, by email.&lt;/p&gt;
&lt;h3 id=&quot;quiz-application&quot;&gt;Quiz Application&lt;/h3&gt;
&lt;p&gt;Knowledge is power. There are so many things in the world to learn, and quizzes help in testing the understanding of those concepts. You, as an intermediate Python developer, do not have to understand everything about the language. Taking tests is one way to find out things you don&amp;rsquo;t fully understand.&lt;/p&gt;
&lt;p&gt;This is where the Quiz Application comes in. The Quiz Application will present questions to the users and expect the right answers to those questions. Think of the Quiz Application as a kind of questionnaire. &lt;/p&gt;
&lt;p&gt;With the Quiz Application, special users you can call administrators will be allowed to create tests, so regular users can answer the questions and test their understanding of the topics in the quiz. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Quiz Applications&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Quiz Application idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://myquiz.org/&quot;&gt;myQuiz&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kahoot.com/&quot;&gt;Kahoot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to set quizzes and have people answer them. Therefore, users should be able to set questions, and other users should be able to answer those questions. The application will then display the final score and the right answers.&lt;/p&gt;
&lt;p&gt;If you want users to be able to have a record of their scores, you may have to implement an account creation feature. &lt;/p&gt;
&lt;p&gt;Users creating the tests should be able to create tests with the questions and answers by simply uploading a text file. The text file will have a format that you can decide, so the application can convert from a file to a quiz.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to implement a database for this project. The database will store the questions, possible answers, correct answers, and the scores for each user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For more of a challenge, you can allow users to add timers to the quizzes. This way, the creators of a quiz can determine how many seconds or minutes a user should spend on each question in the quiz. &lt;/p&gt;
&lt;p&gt;It would be great to also have a quiz-sharing feature, so users can share interesting quizzes with their friends on other platforms.&lt;/p&gt;
&lt;h2 id=&quot;gui-project-ideas&quot;&gt;GUI Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for Graphical User Interfaces. These project ideas can be classified as entertainment, finance, and utility tools. &lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MP3 Player&lt;/li&gt;
&lt;li&gt;Alarm Tool&lt;/li&gt;
&lt;li&gt;File Manager&lt;/li&gt;
&lt;li&gt;Expense Tracker&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;mp3-player&quot;&gt;MP3 Player&lt;/h3&gt;
&lt;p&gt;Audio is as important as text today if not more important. Since audio files are digital files, you&amp;rsquo;ll need a tool that can play them. Without a player, you&amp;rsquo;ll never be able to listen to the contents of an audio file.&lt;/p&gt;
&lt;p&gt;This is where the MP3 Player comes in. The MP3 Player is a device for playing MP3s and other digital audio files. This MP3 Player GUI project idea attempts to emulate the physical MP3 Player. You can build software that allows you play an MP3 files on your desktop or laptop computer.&lt;/p&gt;
&lt;p&gt;When you are done building the MP3 Player project, users can play their MP3 files and other digital audio files without having to purchase a physical MP3 Player. They&amp;rsquo;ll be able to play the MP3 files using their computers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of MP3 Players&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the MP3 Player idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://getmusicbee.com/&quot;&gt;MusicBee&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.foobar2000.org/&quot;&gt;Foobar2000&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to allow users to play MP3 and digital audio files. To be engaging for users, the application has to have a simple but beautiful user interface.&lt;/p&gt;
&lt;p&gt;You can have an interface for listing the available MP3 files. You can also give users the option to list other digital audio files that are not MP3.&lt;/p&gt;
&lt;p&gt;The users will also expect the MP3 Player to have an interface that shows information on the file that is playing. Some of the information you can include are the name of the file, its length, the amount played, and the amount not played, in minutes and seconds.&lt;/p&gt;
&lt;p&gt;Python has libraries that can play audio files, such as &lt;a href=&quot;https://pypi.org/project/Pygame/&quot;&gt;&lt;code&gt;pygame&lt;/code&gt;&lt;/a&gt;, which allows you to work with multimedia files in few lines of code. You can also check out &lt;a href=&quot;https://pypi.org/project/PyMedia/&quot;&gt;&lt;code&gt;pymedia&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://pypi.org/project/simpleaudio/&quot;&gt;&lt;code&gt;simpleaudio&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These libraries can handle a lot of digital audio files. They can handle other file types, not just the MP3 files.&lt;/p&gt;
&lt;p&gt;You can also implement a feature that allows users to create a playlist. To do this, you&amp;rsquo;ll need a database to store information on the created playlists. Python&amp;rsquo;s &lt;code&gt;sqlite3&lt;/code&gt; module allows you to use the SQLite database.&lt;/p&gt;
&lt;p&gt;The SQLite database is a better option in this case, because it is file based and easier to set up than other SQL databases. While SQLite is file based, &lt;a href=&quot;https://stackoverflow.com/questions/19946298/what-is-the-advantage-of-using-sqlite-rather-than-file&quot;&gt;it is better for saving data than a regular file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For a more exciting challenge, you can add a feature to allow the MP3 player to repeat currently playing files or even shuffle the list of files to be played.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also possible to implement a feature that allows users to increase and decrease the playing speed of the audio file. Users will find this interesting, as they&amp;rsquo;ll be able to play files at a slower or faster pace than usual.&lt;/p&gt;
&lt;h3 id=&quot;alarm-tool&quot;&gt;Alarm Tool&lt;/h3&gt;
&lt;p&gt;As they say, &amp;ldquo;Time and tide wait for no man.&amp;rdquo; But with a lot of things going on in our lives, it&amp;rsquo;s difficult to not lose track of time. To be able to keep track of time, a reminder is needed.&lt;/p&gt;
&lt;p&gt;This is where the Alarm Tool comes in. An alarm is a device that gives an audio or visual signal about a certain condition. This Alarm Tool project idea is an attempt to build an alarm as software. The Alarm Tool gives an audio signal when a certain condition is met. The set time is the certain condition in this case.&lt;/p&gt;
&lt;p&gt;With the Alarm Tool, users can set alarms to remind them of things at certain times of the day. The Alarm Tool project will work from the user&amp;rsquo;s laptop or desktop device, so they do not have to purchase a physical timer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Alarm Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Alarm Tool idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://freealarmclocksoftware.com/&quot;&gt;FreeAlarmClock&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.apimac.com/mac/timer/&quot;&gt;TimerForMac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to activate audio signals at certain times of the day. So, timing and the audio signal to be played are the most important parts of the Alarm Tool.&lt;/p&gt;
&lt;p&gt;The Alarm Tool should allow users to create, edit, and delete alarms. It should also have an interface that lists all the alarms, provided they have not being deleted by the user. So, it should list the active and inactive alarms.&lt;/p&gt;
&lt;p&gt;Since it is an alarm, the application has to play tones at the set time. There are libraries for playing audio, like the &lt;a href=&quot;https://pypi.org/project/Pygame/&quot;&gt;&lt;code&gt;pygame&lt;/code&gt;&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;In your code logic, the application has to keep checking for set alarm times. When the time is reached, it triggers a function to play the alarm tone.&lt;/p&gt;
&lt;p&gt;Since the application will check for set alarm times, it means the application has to save the alarms in a database. The database should store things like the alarm date, time, and tone location.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As an extra feature, you can allow users to set recurring alarms. They&amp;rsquo;ll be able to set alarms that will ring at a certain time on certain days of the week, every week. As an example, an alarm can be set at 2:00 PM every Monday.&lt;/p&gt;
&lt;p&gt;You can also add a snooze feature, so your users can snooze alarms instead of only dismissing them.&lt;/p&gt;
&lt;h3 id=&quot;file-manager&quot;&gt;File Manager&lt;/h3&gt;
&lt;p&gt;The number of files on the personal computer of an average PC user is pretty high. If all of those files were placed in a single directory, it would be difficult to navigate and find files or directories. So, there is a need to arrange the files and manage them properly.&lt;/p&gt;
&lt;p&gt;This is where a file manager comes in. A file manager allows users to manage files and directories through a user interface. While files can be managed through the command-line, not all users know how to do that.&lt;/p&gt;
&lt;p&gt;With a file manager, users can arrange, access, and administer their files and directories properly without knowing how to use the command line. Some of the tasks a file manager allows users to perform includes copying, moving, and renaming files or directories.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of File Manager Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the File Manager idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://freecommander.com/en/summary/&quot;&gt;FreeCommander&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://explorerplusplus.com/&quot;&gt;Explorer++&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of the file manager project is to give users an interface to manage their files. Users want a file manager that has a file management tool that looks good and is easy to use. &lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;PySimpleGUI&lt;/code&gt; library to create unique user interfaces with a powerful widget, without having to deal with a lot of complexity.&lt;/p&gt;
&lt;p&gt;Your users should be able to perform simple tasks like creating new directories or empty text files. They should also be able to copy and move files or directories. &lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sys&lt;/code&gt;, &lt;code&gt;os&lt;/code&gt;, and &lt;code&gt;shutil&lt;/code&gt; libraries will be quite useful for this project, as they can be used to execute actions on the files in the background, while the user clicks away.&lt;/p&gt;
&lt;p&gt;The grid and list views are popular views today, so you can implement both in the application. This gives the user the option to choose which view option is suitable for them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To make the file manager a bit more advanced, you can implement a search feature. So users can search for files and directories without having to find them manually.&lt;/p&gt;
&lt;p&gt;You can also implement a sort feature. This will allow users to sort files according to different orders, such as time, alphabetical order, or size.&lt;/p&gt;
&lt;h3 id=&quot;expense-tracker&quot;&gt;Expense Tracker&lt;/h3&gt;
&lt;p&gt;We have daily expenses, from groceries to clothing to bills. There are so many expenses that it&amp;rsquo;s normal to lose track of them and keep spending till we&amp;rsquo;re almost out of cash. A tracker can help people watch their expenses.&lt;/p&gt;
&lt;p&gt;This is where the expense tracker comes in. An expense tracker is a software tool that allows users to keep track of their expenses. It can also analyze the expenses, depending on how advanced it is, but let&amp;rsquo;s keep it simple for now.&lt;/p&gt;
&lt;p&gt;With the expense tracker, users can set a budget and track their spending so as to make better financial decisions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Expense Trackers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Expense Tracker idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://buddi.digitalcave.ca/&quot;&gt;Buddi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gnucash.org/&quot;&gt;GnuCash&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to keep track of the user&amp;rsquo;s expenses. Some statistical analysis has to be done to be able to give users correct information on their expenses and help them spend better.&lt;/p&gt;
&lt;p&gt;While tracking the expenses is the key thing, a good interface is also important. With &lt;code&gt;PySimpleGUI&lt;/code&gt;, you can create a unique interface to improve the experience of the users.&lt;/p&gt;
&lt;p&gt;PyData libraries such as &lt;code&gt;pandas&lt;/code&gt; and &lt;code&gt;matplotlib&lt;/code&gt; can be helpful for building the expense tracker. &lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://pandas.pydata.org/pandas-docs/stable/&quot;&gt;&lt;code&gt;pandas&lt;/code&gt;&lt;/a&gt; library can be used for the data analysis, and the &lt;a href=&quot;https://matplotlib.org/&quot;&gt;&lt;code&gt;matplotlib&lt;/code&gt;&lt;/a&gt; library can be used for plotting graphs. Graphs will give the users a visual representation of their expenses, and a visual representation is usually easier to understand.&lt;/p&gt;
&lt;p&gt;The application will receive data from the users. The data here is the inputted expenses. So, you&amp;rsquo;ll have to store the expenses in a database. The SQLite database is a good database choice for this project since it can be set up quickly. You can use &lt;code&gt;sqlite3&lt;/code&gt; module for the SQLite database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For your users to benefit from this project, they&amp;rsquo;ll have to input their expenses regularly, which might slip their mind. 
It could be useful for you to implement a reminder feature. So the application will send a notification at certain times of the day or the week, reminding them to make use of the expense tracker.&lt;/p&gt;
&lt;h2 id=&quot;command-line-project-ideas&quot;&gt;Command-Line Project Ideas&lt;/h2&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll see project ideas for the command-line. The project ideas discussed can be classified as utility tools. &lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s are the project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Contact Book&lt;/li&gt;
&lt;li&gt;File Connectivity Checker&lt;/li&gt;
&lt;li&gt;Bulk File Rename Tool&lt;/li&gt;
&lt;li&gt;Directory Tree Generator&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;contact-book&quot;&gt;Contact Book&lt;/h3&gt;
&lt;p&gt;We come across lots of people daily. We make acquaintances and friends. We get their contacts to keep in touch later on. Sadly, keeping the received contact details can be hard. One way to do this is to write the contact details down. But this is not secure as the physical book can easily be lost.&lt;/p&gt;
&lt;p&gt;This is where the Contact Book project comes in. A contact book is a tool for saving a contact&amp;rsquo;s details, such as name, address, phone number, and email address. With this contact book project, you can build a software tool that people can use to save and find contact details.&lt;/p&gt;
&lt;p&gt;With the contact book project idea, users can save their contacts with less risk of losing the saved contact details. It&amp;rsquo;ll always be accessible from their computer, through the command-line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Contact Book Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are Contact Book applications, but it&amp;rsquo;s rare to find command-line Contact Book products, as most are web, mobile, or GUI applications.&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Contact Book idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://play.google.com/store/apps/details?id=com.simplemobiletools.contacts&amp;amp;hl=en_US&quot;&gt;Simple Contacts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://connect.pobuca.com/&quot;&gt;Pobuca Connect&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to save contact details. It&amp;rsquo;s important that you set up the commands users can use to enter the contact details. You can use the &lt;code&gt;argparse&lt;/code&gt; or &lt;code&gt;click&lt;/code&gt; command-line frameworks. They abstract a lot of complex stuff, so you only have to focus on the logic to be run when executing commands.&lt;/p&gt;
&lt;p&gt;Some features you should implement include the commands to delete contacts, update contact information, and list saved contacts. You can also allow users to list contacts using different parameters, such as alphabetical order or contact creation date.&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s a command-line project, the SQLite database will be fine for saving contacts. SQLite is user-friendly to set up. You may save the contact details in a file, but a file will not offer the benefits you can gain from using SQLite, such as performance and security.&lt;/p&gt;
&lt;p&gt;To use the SQLite database in this project, the Python &lt;code&gt;sqlite3&lt;/code&gt; module will be very useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Remember how the database is stored on the user&amp;rsquo;s computer? What if something happens, like the user losing their files? It means they&amp;rsquo;ll also lose the contact details.&lt;/p&gt;
&lt;p&gt;You can challenge yourself further and backup the database to an online storage platform. To do this, you can upload the database files to the cloud at certain intervals.&lt;/p&gt;
&lt;p&gt;You can also add a command that allows users to backup the database themselves. This way, the user can still have access to the contacts if the database file is lost. &lt;/p&gt;
&lt;p&gt;You should note that you may need some form of identification, so the contact book can tell which database file belongs to which user. Implementing a user authentication feature is one way to go about it.&lt;/p&gt;
&lt;h3 id=&quot;site-connectivity-checker&quot;&gt;Site Connectivity Checker&lt;/h3&gt;
&lt;p&gt;When you visit a URL, you expect to get the requested pages on your browser. But this is not always the case. Sometimes, sites can be down, so you won&amp;rsquo;t get the desired results. Instead, you&amp;rsquo;ll be presented with error messages. You can keep trying a site that is down, till it comes up and you get the information you need.&lt;/p&gt;
&lt;p&gt;This is where the Site Connectivity Checker project comes in. The Site Connectivity Checker visits a URL and returns the status of the URL: it is either live or not. The Site Connectivity Checker will visit the URL at intervals, returning the results of each visit.&lt;/p&gt;
&lt;p&gt;Instead of manually visiting a URL, a Site Connectivity Checker can do all of that manual work for you. This way, you&amp;rsquo;ll only get the results of the check without having to spend time on the browser, waiting for the site to go live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Site Connectivity Checkers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Site Connectivity Checker idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Ping_(networking_utility)&quot;&gt;Ping&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.site24x7.com/check-website-availability.html&quot;&gt;Site24x7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project is to check the status of sites. So, you need to write code for checking the status of a website.&lt;/p&gt;
&lt;p&gt;You can choose to use either &lt;a href=&quot;https://en.wikipedia.org/wiki/Transmission_Control_Protocol&quot;&gt;TCP&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol&quot;&gt;ICMP&lt;/a&gt; for your connections. The &lt;a href=&quot;https://docs.python.org/3/library/socket.html&quot;&gt;&lt;code&gt;socket&lt;/code&gt;&lt;/a&gt; module is one to check out. You can also read &lt;a href=&quot;https://realpython.com/python-sockets/&quot;&gt;Socket Programming in Python (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Through your chosen framework, be it the &lt;code&gt;docopt&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, or &lt;code&gt;argparse&lt;/code&gt; framework, you can add commands to allow users to add and remove sites from the list of sites to be checked.&lt;/p&gt;
&lt;p&gt;The users should also be able to start the tool, stop it, and determine the intervals.&lt;/p&gt;
&lt;p&gt;Since you&amp;rsquo;ll have to save the list of files to be checked, you can either save it in a file (just a list of sites) or use a SQLite database through the &lt;code&gt;sqlite3&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The application can check for the connectivity status of sites and display the results to the command-line. But this will require the user to keep checking the command-line. &lt;/p&gt;
&lt;p&gt;You can increase the challenge and implement a notification feature. The notification feature can be a sound played in the background to alert the user when a site&amp;rsquo;s status changes. You&amp;rsquo;ll need a database to store the previous status of a site. That&amp;rsquo;s the only way the tool can tell when the status changes.&lt;/p&gt;
&lt;h3 id=&quot;bulk-file-rename-tool&quot;&gt;Bulk File Rename Tool&lt;/h3&gt;
&lt;p&gt;Sometimes, you need to name all the files in a directory according to certain conventions. For example, you can name all the files in a directory with &lt;code&gt;File0001.jpg&lt;/code&gt;, where the numbers increase based on the number of files in the directory. Doing this manually can be stressful and repetitive.&lt;/p&gt;
&lt;p&gt;The Bulk File Rename Tool allows users to rename a large number of files, without having to manually rename files.&lt;/p&gt;
&lt;p&gt;This saves users a lot of time. It spares them the trouble of having to do boring repetitive work and make mistakes. With the Bulk File Rename Tool, users can rename files in a couple of seconds without any mistakes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Bulk File Rename Tools&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Bulk File Rename idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Ren_(command)&quot;&gt;Ren&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tecmint.com/rename-multiple-files-in-linux/&quot;&gt;Rename&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of this project idea is to rename files. So, the application needs to find a way to manipulate the target files. The &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;sys&lt;/code&gt;, and &lt;code&gt;shutil&lt;/code&gt; libraries will be useful for a large part of this project.&lt;/p&gt;
&lt;p&gt;Your users will be able to rename all the files in the directory, using naming conventions. Therefore, they should be able to pass in the naming convention of choice. The &lt;code&gt;regex&lt;/code&gt; module will help match the required naming patterns, if you understand how regex works.&lt;/p&gt;
&lt;p&gt;A user may want to pass in a naming convention such as &lt;code&gt;myfiles&lt;/code&gt; as part of the commands and expect that the tool renames all the files like &lt;code&gt;myfilesXYZ&lt;/code&gt;, where &lt;code&gt;XYZ&lt;/code&gt; is a number. They should also be able to choose the directory where the files to be renamed are.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The major challenge in this project is to rename all the files in a directory. But users may only need to name a certain number of files. To test your skills, you can implement a feature to allow users to choose the number of files to be renamed, instead of all the files. &lt;/p&gt;
&lt;p&gt;Note that renaming only a certain number of files will require the tool to sort the files based on alphabetical order, time of file creation, or file size, depending on the user&amp;rsquo;s requirements. &lt;/p&gt;
&lt;h3 id=&quot;directory-tree-generator&quot;&gt;Directory Tree Generator&lt;/h3&gt;
&lt;p&gt;Directories are like family trees: each directory has a particular relationship with other directories. No directories ever stays on its own, except an empty root directory. &lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re working with files and directories, it is difficult to see the relationship between directories, as you can only see what exists in the current directory. You&amp;rsquo;re either using a file manager or working from the command-line. &lt;/p&gt;
&lt;p&gt;With a Directory Tree Generator, you can see the relationship between files and directories like a tree or a map. &lt;/p&gt;
&lt;p&gt;This makes it easier to understand the positioning of files and directories. A directory tree map is important when you&amp;rsquo;re explaining certain concepts, and a Directory Tree Generator makes it easier to get a visual representation of the file and directory relationships.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples of Directory Tree Generators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are some implementations of the Directory Tree Generator idea:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_(command)&quot;&gt;Tree&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mauriziodimatteo/dirtreex&quot;&gt;Dirtreex&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Technical Details&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main objective of the Directory Tree Generator is to visualize the relationships between files and directories. The &lt;code&gt;os&lt;/code&gt; library can be very useful in listing the files and directories in a chosen directory.&lt;/p&gt;
&lt;p&gt;Using a framework such as &lt;code&gt;docopt&lt;/code&gt; or &lt;code&gt;argparse&lt;/code&gt; helps abstract a lot of stuff, allowing you to focus on writing code for the application&amp;rsquo;s logic.&lt;/p&gt;
&lt;p&gt;In the application&amp;rsquo;s logic, you can decide how you want to represent files or directories. Using different colors is a brilliant way to go about it. You can use the &lt;code&gt;colored&lt;/code&gt; library to print the files and directories in different colors. &lt;/p&gt;
&lt;p&gt;You can also decide how deep you&amp;rsquo;d like the Directory Tree Generator to go. For example, if a directory has children directories twelve levels deep, you may decide to go only as deep as the fifth level.&lt;/p&gt;
&lt;p&gt;If you wish, you can also let the user decide how deep they want the Directory Tree Generator to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extra Challenge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since the results of the generated directory tree will be on the command-line, you can go one step further. You can have the generator create images of the directory tree, so it&amp;rsquo;ll basically turn the text into an image. &lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll find the &lt;a href=&quot;https://pillow.readthedocs.io/en/stable/&quot;&gt;&lt;code&gt;pillow&lt;/code&gt;&lt;/a&gt; library useful for doing this.&lt;/p&gt;
&lt;h2 id=&quot;tips-for-working-on-projects&quot;&gt;Tips for Working on Projects&lt;/h2&gt;
&lt;p&gt;Working on projects can be difficult. That&amp;rsquo;s one reason why motivation and interest in a project will make it a less daunting task.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in a project, you&amp;rsquo;ll be able to put in the time to research as well as find libraries and tools that will help you with the project.&lt;/p&gt;
&lt;p&gt;Here are some tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find a source of motivation&lt;/li&gt;
&lt;li&gt;Break the project into subtasks&lt;/li&gt;
&lt;li&gt;Do research on the subtasks&lt;/li&gt;
&lt;li&gt;Build each subtasks, one step at a time&lt;/li&gt;
&lt;li&gt;Reach out for help if you&amp;rsquo;re stuck&lt;/li&gt;
&lt;li&gt;Put the subtasks together&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, you&amp;rsquo;ve seen a couple of Python project ideas you may find interesting. &lt;/p&gt;
&lt;p&gt;The project ideas cover a range of platforms. You saw project ideas for the Web, GUI, and Command-line platforms. &lt;/p&gt;
&lt;p&gt;You can choose to build a project for different platforms. Using the URL Shortener as an example, you may choose to build one for the Web, GUI, or the Command-line.&lt;/p&gt;
&lt;p&gt;Since you&amp;rsquo;re an intermediate Python developer, these projects can be quite challenging but interesting.&lt;/p&gt;
&lt;p&gt;The best way to make a project happen is to just get started. In no time, you&amp;rsquo;ll be finished and discover how much you&amp;rsquo;ve benefited from working on a project!&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Decorators 101</title>
      <id>https://realpython.com/courses/python-decorators-101/</id>
      <link href="https://realpython.com/courses/python-decorators-101/"/>
      <updated>2019-03-19T14:00:00+00:00</updated>
      <summary>In this course on Python decorators, you&#39;ll learn what they are and how to create and use them. Decorators provide a simple syntax for calling higher-order functions in Python. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.</summary>
      <content type="html">
        &lt;p&gt;In this course on Python decorators, you&amp;rsquo;ll learn what they are and how to create and use them. Decorators provide a simple syntax for calling higher-order functions in Python. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Decorators Slack Q&amp;amp;A With the Real Python Team&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re hosting a Python decorators Q&amp;amp;A session with Chris Bailey, Geir Arne Hjelle, and Dan Bader on the &lt;a href=&quot;/join&quot;&gt;members-only&lt;/a&gt; Real Python Community Slack on &lt;strong&gt;Thursday, March 28 1pm-2pm EST&lt;/strong&gt; (&lt;a href=&quot;https://www.timeanddate.com/worldclock/fixedtime.html?msg=AMA+Python+Decorators&amp;amp;iso=20190328T17&amp;amp;p1=1440&amp;amp;ah=1&quot;&gt;click here to see when that is in your local timezone&lt;/a&gt;). &lt;/p&gt;
&lt;p&gt;Join us to talk about all things decorators and how they can make your code more Pythonic. See you on Slack :)&lt;/p&gt;
&lt;/div&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>How to Build a Python GUI Application With wxPython</title>
      <id>https://realpython.com/python-gui-with-wxpython/</id>
      <link href="https://realpython.com/python-gui-with-wxpython/"/>
      <updated>2019-03-18T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to create a cross-platform graphical user interface (GUI) using Python and the wxPython toolkit. A graphical user interface is an application that has buttons, windows, and lots of other widgets that the user can use to interact with your application.</summary>
      <content type="html">
        &lt;p&gt;There are many graphical user interface (GUI) toolkits that you can use with the Python programming language. The big three are Tkinter, wxPython, and PyQt. Each of these toolkits will work with Windows, macOS, and Linux, with PyQt having the additional capability of working on mobile. &lt;/p&gt;
&lt;p&gt;A graphical user interface is an application that has buttons, windows, and lots of other widgets that the user can use to interact with your application. A good example would be a web browser. It has buttons, tabs, and a main window where all the content loads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to build a graphical user interface with Python using the wxPython GUI toolkit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here are the topics covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Getting Started with wxPython&lt;/li&gt;
&lt;li&gt;Definition of a GUI&lt;/li&gt;
&lt;li&gt;Creating a Skeleton Application&lt;/li&gt;
&lt;li&gt;Creating a Working Application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s start learning!&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-tricks-sample&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a chapter from Python Tricks: The Book&lt;/a&gt; that shows you Python&#39;s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;getting-started-with-wxpython&quot;&gt;Getting Started With wxPython&lt;/h2&gt;
&lt;p&gt;The wxPython GUI toolkit is a Python wrapper around a C++ library called &lt;a href=&quot;https://www.wxwidgets.org/&quot;&gt;wxWidgets&lt;/a&gt;. The initial release of wxPython was in 1998, so wxPython has been around quite a long time. wxPython&amp;rsquo;s primary difference from other toolkits, such as &lt;strong&gt;PyQt&lt;/strong&gt; or &lt;strong&gt;Tkinter&lt;/strong&gt;, is that wxPython uses the actual widgets on the native platform whenever possible. This makes wxPython applications look native to the operating system that it is running on.&lt;/p&gt;
&lt;p&gt;PyQt and Tkinter both draw their widgets themselves, which is why they don&amp;rsquo;t always match the native widgets, although PyQt is very close.&lt;/p&gt;
&lt;p&gt;This is not to say that wxPython does not support custom widgets. In fact, the wxPython toolkit has many custom widgets included with it, along with dozens upon dozens of core widgets. The wxPython &lt;a href=&quot;https://wxpython.org/pages/downloads/&quot;&gt;downloads&lt;/a&gt; page has a section called &lt;strong&gt;Extra Files&lt;/strong&gt; that is worth checking out. &lt;/p&gt;
&lt;p&gt;Here, there is a download of the wxPython Demo package. This is a nice little application that demonstrates the vast majority of the widgets that are included with wxPython. The demo allows a developer to view the code in one tab and run it in a second tab. You can even edit and re-run the code in the demo to see how your changes affect the application.&lt;/p&gt;
&lt;h3 id=&quot;installing-wxpython&quot;&gt;Installing wxPython&lt;/h3&gt;
&lt;p&gt;You will be using the latest wxPython for this article, which is &lt;strong&gt;wxPython 4&lt;/strong&gt;, also known as the Phoenix release. The wxPython 3 and wxPython 2 versions are built only for &lt;strong&gt;Python 2&lt;/strong&gt;. When Robin Dunn, the primary maintainer of wxPython, created the wxPython 4 release, he deprecated a lot of aliases and cleaned up a lot of code to make wxPython more Pythonic and easier to maintain. &lt;/p&gt;
&lt;p&gt;You will want to consult the following links if you are migrating from an older version of wxPython to wxPython 4 (Phoenix):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/classic_vs_phoenix.html&quot;&gt;Classic vs Phoenix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;wxPython Project Phoenix &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/MigrationGuide.html&quot;&gt;Migration Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The wxPython 4 package is compatible with both Python 2.7 and Python 3.&lt;/p&gt;
&lt;p&gt;You can now use &lt;code&gt;pip&lt;/code&gt; to install wxPython 4, which was not possible in the legacy versions of wxPython. You can do the following to install it on your machine:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install wxpython
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Mac OS X you will need a compiler installed such as &lt;strong&gt;XCode&lt;/strong&gt; for the install to complete successfully. Linux may also require you to install some dependencies before the &lt;code&gt;pip&lt;/code&gt; installer will work correctly. &lt;/p&gt;
&lt;p&gt;For example, I needed to install &lt;strong&gt;freeglut3-dev&lt;/strong&gt;, &lt;strong&gt;libgstreamer-plugins-base0.10-dev&lt;/strong&gt;, and &lt;strong&gt;libwebkitgtk-3.0-dev&lt;/strong&gt; on Xubuntu to get it to install. &lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Fortunately, the error messages that &lt;code&gt;pip&lt;/code&gt; displays are helpful in figuring out what is missing, and you can use the &lt;a href=&quot;https://github.com/wxWidgets/Phoenix/blob/master/README.rst#prerequisites&quot;&gt;prerequisites&lt;/a&gt; section on the wxPython Github page to help you find the information you need if you want to install wxPython on Linux.&lt;/p&gt;
&lt;p&gt;There are some Python wheels available for the most popular Linux versions that you can find in the &lt;a href=&quot;https://extras.wxpython.org/wxPython4/extras/linux&quot;&gt;Extras Linux&lt;/a&gt; section with both GTK2 and GTK3 versions. To install one of these wheels, you would use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04/ wxPython
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Be sure you have modified the command above to match your version of Linux.&lt;/p&gt;
&lt;h3 id=&quot;definition-of-a-gui&quot;&gt;Definition of a GUI&lt;/h3&gt;
&lt;p&gt;As was mentioned in the introduction, a graphical user interface (GUI) is an interface that is drawn on the screen for the user to interact with.&lt;/p&gt;
&lt;p&gt;User interfaces have some common components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Main window&lt;/li&gt;
&lt;li&gt;Menu&lt;/li&gt;
&lt;li&gt;Toolbar&lt;/li&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Text Entry&lt;/li&gt;
&lt;li&gt;Labels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these items are known generically as &lt;strong&gt;widgets&lt;/strong&gt;. There are many other common widgets and many custom widgets that wxPython supports. A developer will take the widgets and arrange them logically on a window for the user to interact with.&lt;/p&gt;
&lt;h3 id=&quot;event-loops&quot;&gt;Event Loops&lt;/h3&gt;
&lt;p&gt;A graphical user interface works by waiting for the user to do something. The something is called an &lt;strong&gt;event&lt;/strong&gt;. Events happen when the user types something while your application is in focus or when the user uses their mouse to press a button or other widget. &lt;/p&gt;
&lt;p&gt;Underneath the covers, the GUI toolkit is running an infinite loop that is called an &lt;strong&gt;event loop&lt;/strong&gt;. The event loop just waits for events to occur and then acts on those events according to what the developer has coded the application to do. When the application doesn&amp;rsquo;t catch an event, it effectively ignores that it even happened.&lt;/p&gt;
&lt;p&gt;When you are programming a graphical user interface, you will want to keep in mind that you will need to hook up each of the widgets to &lt;strong&gt;event handlers&lt;/strong&gt; so that your application will do something. &lt;/p&gt;
&lt;p&gt;There is a special consideration that you need to keep in mind when working with event loops: they can be blocked. When you block an event loop, the GUI will become unresponsive and appear to freeze to the user. &lt;/p&gt;
&lt;p&gt;Any process that you launch in a GUI that will take longer than a quarter second should probably be launched as a separate thread or process. This will prevent your GUI from freezing and give the user a better user experience. &lt;/p&gt;
&lt;p&gt;The wxPython framework has special thread-safe methods that you can use to communicate back to your application to let it know that the thread is finished or to give it an update.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a skeleton application to demonstrate how events work.&lt;/p&gt;
&lt;h2 id=&quot;creating-a-skeleton-application&quot;&gt;Creating a Skeleton Application&lt;/h2&gt;
&lt;p&gt;An application skeleton in a GUI context is a user interface with widgets that don&amp;rsquo;t have any event handlers. These are useful for prototyping. You basically just create the GUI and present it to your stakeholders for sign-off before spending a lot of time on the backend logic.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start by creating a &lt;code&gt;Hello World&lt;/code&gt; application with wxPython:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Mac users may get the following message: &lt;em&gt;This program needs access to the screen. Please run with a Framework build of python, and only when you are logged in on the main display of your Mac.&lt;/em&gt; If you see this message and you are not running in a virtualenv, then you need to run your application with &lt;strong&gt;pythonw&lt;/strong&gt; instead of &lt;strong&gt;python&lt;/strong&gt;. If you are running wxPython from within a virtualenv, then see the &lt;a href=&quot;https://wiki.wxpython.org/wxPythonVirtualenvOnMac&quot;&gt;wxPython wiki&lt;/a&gt; for the solution.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In this example, you have two parts: &lt;code&gt;wx.App&lt;/code&gt; and the &lt;code&gt;wx.Frame&lt;/code&gt;. The &lt;code&gt;wx.App&lt;/code&gt; is wxPython&amp;rsquo;s application object and is required for running your GUI. The &lt;code&gt;wx.App&lt;/code&gt; starts something called a &lt;code&gt;.MainLoop()&lt;/code&gt;. This is the event loop that you learned about in the previous section. &lt;/p&gt;
&lt;p&gt;The other piece of the puzzle is &lt;code&gt;wx.Frame&lt;/code&gt;, which will create a window for the user to interact with. In this case, you told wxPython that the frame has no parent and that its title is &lt;code&gt;Hello World&lt;/code&gt;. Here is what it looks like when you run the code:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello1.c3e5d5e1cdb9.png&amp;amp;w=100&amp;amp;sig=5914916bf0badc17d5f8d3f6e00e32cb19ad0cd1 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello1.c3e5d5e1cdb9.png&amp;amp;w=200&amp;amp;sig=bbfd92446bb1916ee56ee4db5d60e4a2eebddbef 200w, https://files.realpython.com/media/hello1.c3e5d5e1cdb9.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The application will look different when you run it on Mac or Windows.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;By default, a &lt;code&gt;wx.Frame&lt;/code&gt; will include minimize, maximize, and exit buttons along the top. You won&amp;rsquo;t normally create an application in this manner though. Most wxPython code will require you to subclass the &lt;code&gt;wx.Frame&lt;/code&gt; and other widgets so that you can get the full power of the toolkit. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a moment and rewrite your code as a class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use this code as a template for your application. However, this application doesn&amp;rsquo;t do very much, so let&amp;rsquo;s take a moment to learn a little about some of the other widgets you could add.&lt;/p&gt;
&lt;h3 id=&quot;widgets&quot;&gt;Widgets&lt;/h3&gt;
&lt;p&gt;The wxPython toolkit has more than one hundred widgets to choose from. This allows you to create rich applications, but it can also be daunting trying to figure out which widget to use. This is why the &lt;strong&gt;wxPython Demo&lt;/strong&gt; is helpful, as it has a search filter that you can use to help you find the widgets that might apply to your project.&lt;/p&gt;
&lt;p&gt;Most GUI applications allow the user to enter some text and press a button. Let&amp;rsquo;s go ahead and add those widgets:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you run this code, your application should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/hello2.4b3bac9c90eb.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/hello2.4b3bac9c90eb.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello2.4b3bac9c90eb.png&amp;amp;w=100&amp;amp;sig=c943d9b392d5c56daa09c61b979236aec515bcbe 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/hello2.4b3bac9c90eb.png&amp;amp;w=200&amp;amp;sig=5b0b2d5b47ffb7b1c762295e9dcf158f0194216c 200w, https://files.realpython.com/media/hello2.4b3bac9c90eb.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython with widgets&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first widget you need to add is something called &lt;code&gt;wx.Panel&lt;/code&gt;. This widget is not required, but recommended. On Windows, you are actually required to use a Panel so that the background color of the frame is the right shade of gray. Tab traversal is disabled without a Panel on Windows.&lt;/p&gt;
&lt;p&gt;When you add the panel widget to a frame and the panel is the sole child of the frame, it will automatically expand to fill the frame with itself.&lt;/p&gt;
&lt;p&gt;The next step is to add a &lt;code&gt;wx.TextCtrl&lt;/code&gt; to the panel. The first argument for almost all widgets is which parent the widget should go onto. In this case, you want the text control and the button to be on top of the panel, so it is the parent you specify. &lt;/p&gt;
&lt;p&gt;You also need to tell wxPython where to place the widget, which you can do by passing in a position via the &lt;code&gt;pos&lt;/code&gt; parameter. In wxPython, the origin location is (0,0) which is the upper left corner of the parent. So for the text control, you tell wxPython that you want to position its top left corner 5 pixels from the left (x) and 5 pixels from the top (y).&lt;/p&gt;
&lt;p&gt;Then you add your button to the panel and give it a label. To prevent the widgets from overlapping, you need to set the y-coordinate to 55 for the button&amp;rsquo;s position.&lt;/p&gt;
&lt;h3 id=&quot;absolute-positioning&quot;&gt;Absolute Positioning&lt;/h3&gt;
&lt;p&gt;When you provide exact coordinates for your widget&amp;rsquo;s position, the technique that you used is called &lt;strong&gt;absolute positioning&lt;/strong&gt;. Most GUI toolkits provide this capability, but it&amp;rsquo;s not actually recommended.&lt;/p&gt;
&lt;p&gt;As your application becomes more complex, it becomes difficult to keep track of all the widget locations and if you have to move the widgets around. Resetting all those positions becomes a nightmare.&lt;/p&gt;
&lt;p&gt;Fortunately all modern GUI toolkits provide a solution for this, which is what you will learn about next.&lt;/p&gt;
&lt;h3 id=&quot;sizers-dynamic-sizing&quot;&gt;Sizers (Dynamic Sizing)&lt;/h3&gt;
&lt;p&gt;The wxPython toolkit includes &lt;strong&gt;sizers&lt;/strong&gt;, which are used for creating dynamic layouts. They manage the placement of your widgets for you and will adjust them when you resize the application window. Other GUI toolkits will refer to sizers as layouts, which is what PyQt does. &lt;/p&gt;
&lt;p&gt;Here are the primary types of sizers that you will see used most often:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wx.BoxSizer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.GridSizer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.FlexGridSizer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s add a &lt;code&gt;wx.BoxSizer&lt;/code&gt; to your example and see if we can make it work a bit more nicely:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you create an instance of a &lt;code&gt;wx.BoxSizer&lt;/code&gt; and pass it &lt;code&gt;wx.VERTICAL&lt;/code&gt;, which is the orientation that widgets are added to the sizer.&lt;/p&gt;
&lt;p&gt;In this case, the widgets will be added vertically, which means they will be added one at a time from top to bottom. You may also set a BoxSizer&amp;rsquo;s orientation to &lt;code&gt;wx.HORIZONTAL&lt;/code&gt;. When you do that, the widgets would be added from left to right.&lt;/p&gt;
&lt;p&gt;To add a widget to a sizer, you will use &lt;code&gt;.Add()&lt;/code&gt;. It accepts up to five arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window&lt;/code&gt; (the widget)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;proportion&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flag&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;border&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;userData&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;window&lt;/code&gt; argument is the widget to be added while &lt;code&gt;proportion&lt;/code&gt; sets how much space relative to other widgets in the sizer this particular widget should take. By default, it is zero, which tells wxPython to leave the widget at its default proportion.&lt;/p&gt;
&lt;p&gt;The third argument is &lt;code&gt;flag&lt;/code&gt;. You can actually pass in multiple flags if you wish as long as you separate them with a pipe character: &lt;code&gt;|&lt;/code&gt;. The wxPython toolkit uses &lt;code&gt;|&lt;/code&gt; to add flags using a series of bitwise ORs.&lt;/p&gt;
&lt;p&gt;In this example, you add the text control with the &lt;code&gt;wx.ALL&lt;/code&gt; and &lt;code&gt;wx.EXPAND&lt;/code&gt; flags. The &lt;code&gt;wx.ALL&lt;/code&gt; flag tells wxPython that you want to add a border on all sides of the widget while &lt;code&gt;wx.EXPAND&lt;/code&gt; makes the widgets expand as much as they can within the sizer.&lt;/p&gt;
&lt;p&gt;Finally, you have the &lt;code&gt;border&lt;/code&gt; parameter, which tells wxPython how many pixels of border you want around the widget. The &lt;code&gt;userData&lt;/code&gt; parameter is only used when you want to do something complex with your sizing of the widget and is actually quite rare to see in practice.&lt;/p&gt;
&lt;p&gt;Adding the button to the sizer follows the exact same steps. However, to make things a bit more interesting, I went ahead and switched out the &lt;code&gt;wx.EXPAND&lt;/code&gt; flag for &lt;code&gt;wx.CENTER&lt;/code&gt; so that the button would be centered on-screen.&lt;/p&gt;
&lt;p&gt;When you run this version of the code, your application should look like the following:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/sizers.9f6c203121e6.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/sizers.9f6c203121e6.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/sizers.9f6c203121e6.png&amp;amp;w=100&amp;amp;sig=ef9024be1b46ebd2e2636d963ed13733df364e6d 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/sizers.9f6c203121e6.png&amp;amp;w=200&amp;amp;sig=c246e06c9963cf0541f874c5df1d67f0aa497f53 200w, https://files.realpython.com/media/sizers.9f6c203121e6.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;Hello World in wxPython with Sizers&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to learn more about sizers, the &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/sizers_overview.html&quot;&gt;wxPython documentation&lt;/a&gt; has a nice page on the topic.&lt;/p&gt;
&lt;h3 id=&quot;adding-an-event&quot;&gt;Adding an Event&lt;/h3&gt;
&lt;p&gt;While your application looks more interesting visually, it still doesn&amp;rsquo;t really do anything. For example, if you press the button, nothing really happens. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s give the button a job:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Press Me&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_press&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_press&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;You didn&amp;#39;t enter anything!&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;You typed: &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{value}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MyFrame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The widgets in wxPython allow you to attach event bindings to them so that they can respond to certain types of events.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The code block above uses f-strings. You can read all about them in &lt;a href=&quot;https://realpython.com/python-f-strings/&quot;&gt;Python 3&amp;rsquo;s f-Strings: An Improved String Formatting Syntax (Guide)&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You want the button to do something when the user presses it. You can accomplish this by calling the button&amp;rsquo;s &lt;code&gt;.Bind()&lt;/code&gt; method. &lt;code&gt;.Bind()&lt;/code&gt; takes the event you want to bind to, the handler to call when the event happens, an optional source, and a couple of optional ids. &lt;/p&gt;
&lt;p&gt;In this example, you bind your button object to the &lt;code&gt;wx.EVT_BUTTON&lt;/code&gt; event and tell it to call &lt;code&gt;on_press()&lt;/code&gt; when that event gets fired. &lt;/p&gt;
&lt;p&gt;An event gets &amp;ldquo;fired&amp;rdquo; when the user does the event you have bound to. In this case, the event that you set up is the button press event, &lt;code&gt;wx.EVT_BUTTON&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;&lt;code&gt;.on_press()&lt;/code&gt; accepts a second argument that you can call &lt;code&gt;event&lt;/code&gt;. This is by convention. You could call it something else if you wanted to. However, the event parameter here refers to the fact that when this method is called, its second argument should be an event object of some sort. &lt;/p&gt;
&lt;p&gt;Within &lt;code&gt;.on_press()&lt;/code&gt;, you can get the text control&amp;rsquo;s contents by calling its &lt;code&gt;GetValue()&lt;/code&gt; method. You then print a string to stdout depending on what the contents of the text control is.&lt;/p&gt;
&lt;p&gt;Now that you have the basics out of the way, let&amp;rsquo;s learn how to create an application that does something useful!&lt;/p&gt;
&lt;h2 id=&quot;creating-a-working-application&quot;&gt;Creating a Working Application&lt;/h2&gt;
&lt;p&gt;The first step when creating something new is to figure out what you want to create. In this case, I have taken the liberty of making that decision for you. You will learn how to create a MP3 tag editor! The next step when creating something new is to find out what packages can help you accomplish your task. &lt;/p&gt;
&lt;p&gt;If you do a Google search for &lt;code&gt;Python mp3 tagging&lt;/code&gt;, you will find you have several options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mp3-tagger&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eyeD3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutagen&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tried out a couple of these and decided that &lt;strong&gt;eyeD3&lt;/strong&gt; had a nice API that you could use without getting bogged down with the MP3&amp;rsquo;s ID3 specification. You can install &lt;strong&gt;eyeD3&lt;/strong&gt; using &lt;code&gt;pip&lt;/code&gt;, like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install eyed3
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When installing this package on macOS, you may need to install &lt;code&gt;libmagic&lt;/code&gt; using &lt;code&gt;brew&lt;/code&gt;. Windows and Linux users shouldn&amp;rsquo;t have any issues installing eyeD3.&lt;/p&gt;
&lt;h3 id=&quot;designing-the-user-interface&quot;&gt;Designing the User Interface&lt;/h3&gt;
&lt;p&gt;When it comes to designing an interface, it&amp;rsquo;s always nice to just kind of sketch out how you think the user interface should look. &lt;/p&gt;
&lt;p&gt;You will need to be able to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open up one or more MP3 files&lt;/li&gt;
&lt;li&gt;Display the current MP3 tags&lt;/li&gt;
&lt;li&gt;Edit an MP3 tag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most user interfaces use a menu or a button for opening files or folders. You can go with a &lt;em&gt;File&lt;/em&gt; menu for this. Since you will probably want to see tags for multiple MP3 files, you will need to find a widget that can do this in a nice manner.&lt;/p&gt;
&lt;p&gt;Something that is tabular with columns and rows would be ideal because then you can have labeled columns for the MP3 tags. The wxPython toolkit has a few widgets that would work for this, with the top two being the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wx.grid.Grid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wx.ListCtrl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should use &lt;code&gt;wx.ListCtrl&lt;/code&gt; in this case as the &lt;code&gt;Grid&lt;/code&gt; widget is overkill, and frankly it is also quite a bit more complex. Finally, you need a button to use to edit a selected MP3&amp;rsquo;s tag. &lt;/p&gt;
&lt;p&gt;Now that you know what you want, you can draw it up:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/mockup.b145e58828b0.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/mockup.b145e58828b0.png&quot; width=&quot;450&quot; height=&quot;400&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mockup.b145e58828b0.png&amp;amp;w=112&amp;amp;sig=fa9a713da01a3ebc15fc341b764af9c3c6ea353d 112w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/mockup.b145e58828b0.png&amp;amp;w=225&amp;amp;sig=6bfcb6378599846b10ba467612edad6505a41afb 225w, https://files.realpython.com/media/mockup.b145e58828b0.png 450w&quot; sizes=&quot;75vw&quot; alt=&quot;MP3 Editor in wxPython&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The illustration above gives us an idea of how the application should look. Now that you know what you want to do, it&amp;rsquo;s time to code!&lt;/p&gt;
&lt;h3 id=&quot;creating-the-user-interface&quot;&gt;Creating the User Interface&lt;/h3&gt;
&lt;p&gt;There are many different approaches when it comes to writing a new application. For example, do you need to follow the &lt;a href=&quot;https://en.wikipedia.org/wiki/Model-view-controller&quot;&gt;Model-View-Controller&lt;/a&gt; design pattern? How do you split up the classes? One class per file? There are many such questions, and as you get more experienced with GUI design, you&amp;rsquo;ll know how you want to answer them. &lt;/p&gt;
&lt;p&gt;In your case, you really only need two classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;wx.Panel&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;wx.Frame&lt;/code&gt; class&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You could argue for creating a controller type module as well, but for something like this, you really do not need it. A case could also be made for putting each class into its own module, but to keep it compact, you will create a single Python file for all of your code. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with imports and the panel class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;eyed3&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;glob&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;wx&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LC_REPORT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BORDER_SUNKEN&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Edit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;edit_button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;in on_edit&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you import the &lt;code&gt;eyed3&lt;/code&gt; package, Python&amp;rsquo;s &lt;code&gt;glob&lt;/code&gt; package, and the &lt;code&gt;wx&lt;/code&gt; package for your user interface. Next, you subclass &lt;code&gt;wx.Panel&lt;/code&gt; and create your user interface. You need a dictionary for storing data about your MP3s, which you can name &lt;code&gt;row_obj_dict&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Then you create a &lt;code&gt;wx.ListCtrl&lt;/code&gt; and set it to report mode (&lt;code&gt;wx.LC_REPORT&lt;/code&gt;) with a sunken border (&lt;code&gt;wx.BORDER_SUNKEN&lt;/code&gt;). The list control can take on a few other forms depending on the style flag that you pass in, but the report flag is the most popular.&lt;/p&gt;
&lt;p&gt;To make the &lt;code&gt;ListCtrl&lt;/code&gt; have the correct headers, you will need to call &lt;code&gt;.InsertColumn()&lt;/code&gt; for each column header. You then supply the index of the column, its label, and how wide in pixels the column should be.&lt;/p&gt;
&lt;p&gt;The last step is to add your &lt;code&gt;Edit&lt;/code&gt; button, an event handler, and a method. You can create the binding to the event and leave the method that it calls empty for now.&lt;/p&gt;
&lt;p&gt;Now you should write the code for the frame:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                         &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Mp3 Tag Editor&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;frame&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MainLoop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This class is much simpler than the first one in that all you need to do is set the title of the frame and instantiate the panel class, &lt;code&gt;Mp3Panel&lt;/code&gt;. When you are all done, your user interface should look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/tag_editor.01b0ac1ff939.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/tag_editor.01b0ac1ff939.png&quot; width=&quot;400&quot; height=&quot;250&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tag_editor.01b0ac1ff939.png&amp;amp;w=100&amp;amp;sig=ec89c862dfd2d32db070070feef77de9543b6651 100w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/tag_editor.01b0ac1ff939.png&amp;amp;w=200&amp;amp;sig=b9be5e771a35e8d0b3de01b06b00b7f855b97229 200w, https://files.realpython.com/media/tag_editor.01b0ac1ff939.png 400w&quot; sizes=&quot;75vw&quot; alt=&quot;wxPython MP3 Tag Editor&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The user interface looks almost right, but you don&amp;rsquo;t have a &lt;em&gt;File&lt;/em&gt; menu. This makes it impossible to add MP3s to the application and edit their tags!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s fix that now.&lt;/p&gt;
&lt;h3 id=&quot;make-a-functioning-application&quot;&gt;Make a Functioning Application&lt;/h3&gt;
&lt;p&gt;The first step in making your application work is to update the application so that it has a &lt;em&gt;File&lt;/em&gt; menu because then you can add MP3 files to your creation. Menus are almost always added to the &lt;code&gt;wx.Frame&lt;/code&gt; class, so that is the class you need to modify.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some applications have moved away from having menus in their applications. One of the first to do so was Microsoft Office when they added the Ribbon Bar. The wxPython toolkit has a custom widget that you can use to create ribbons in &lt;code&gt;wx.lib.agw.ribbon&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The other type of application that has dropped menus of late are web browsers, such as Google Chrome and Mozilla Firefox. They just use toolbars nowadays.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s learn how to add a menu bar to our application:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Mp3Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Frame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                          &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Mp3 Tag Editor&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mp3Panel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MenuBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;open_folder_menu_item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_ANY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Open Folder&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;Open a folder with MP3s&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file_menu&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;amp;File&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_MENU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open_folder_menu_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetMenuBar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;menu_bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Choose a directory:&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                           &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DD_DEFAULT_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you add a call to &lt;code&gt;.create_menu()&lt;/code&gt; within the class&amp;rsquo;s constructor. Then in &lt;code&gt;.create_menu()&lt;/code&gt; itself, you will create a &lt;code&gt;wx.MenuBar&lt;/code&gt; instance and a &lt;code&gt;wx.Menu&lt;/code&gt; instance. &lt;/p&gt;
&lt;p&gt;To add a menu item to a menu, you call the menu instance&amp;rsquo;s &lt;code&gt;.Append()&lt;/code&gt; and pass it the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A unique identifier&lt;/li&gt;
&lt;li&gt;The label for the new menu item&lt;/li&gt;
&lt;li&gt;A help string&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, you need to add the menu to the menubar, so you will need to call the menubar&amp;rsquo;s &lt;code&gt;.Append()&lt;/code&gt;. It takes the menu instance and the label for menu. This label is a bit odd in that you called it &lt;code&gt;&amp;amp;File&lt;/code&gt; instead of &lt;code&gt;File&lt;/code&gt;. The ampersand tells wxPython to create a keyboard shortcut of &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-alt&quot;&gt;Alt&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-f&quot;&gt;F&lt;/kbd&gt;&lt;/span&gt; to open the &lt;code&gt;File&lt;/code&gt; menu using just your keyboard.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you would like to add keyboard shortcuts to your application, then you will want to use an instance of &lt;code&gt;wx.AcceleratorTable&lt;/code&gt; to create them. You can read more about Accerator Tables in the wxPython &lt;a href=&quot;https://wxpython.org/Phoenix/docs/html/wx.AcceleratorTable.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;To create an event binding, you will need to call &lt;code&gt;self.Bind()&lt;/code&gt;, which binds the frame to &lt;code&gt;wx.EVT_MENU&lt;/code&gt;. When you use &lt;code&gt;self.Bind()&lt;/code&gt; for a menu event, you need to not only tell wxPython which &lt;code&gt;handler&lt;/code&gt; to use, but also which &lt;code&gt;source&lt;/code&gt; to bind the handler to. &lt;/p&gt;
&lt;p&gt;Finally, you must call the frame&amp;rsquo;s &lt;code&gt;.SetMenuBar()&lt;/code&gt; and pass it the menubar instance for it to be shown to the user.&lt;/p&gt;
&lt;p&gt;Now that you have the menu added to your frame, let&amp;rsquo;s go over the menu item&amp;rsquo;s event handler, which is reproduced again below:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_open_folder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Choose a directory:&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DirDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DD_DEFAULT_STYLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_OK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;panel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since you want the user to choose a folder that contains MP3s, you will want to use wxPython&amp;rsquo;s &lt;code&gt;wx.DirDialog&lt;/code&gt;. The &lt;code&gt;wx.DirDialog&lt;/code&gt; allows the user to only open directories.&lt;/p&gt;
&lt;p&gt;You can set the dialog&amp;rsquo;s title and various style flags. To show the dialog, you will need to call &lt;code&gt;.ShowModal()&lt;/code&gt;. This will cause the dialog to show modally, which means that the user won&amp;rsquo;t be able to interact with your main application while the dialog is shown.&lt;/p&gt;
&lt;p&gt;If the user presses the dialog&amp;rsquo;s &lt;em&gt;OK&lt;/em&gt; button, you can get the user&amp;rsquo;s path choice via the dialog&amp;rsquo;s &lt;code&gt;.GetPath()&lt;/code&gt;. You will want to pass that path to your panel class, which you can do here by calling the panel&amp;rsquo;s &lt;code&gt;.update_mp3_listing()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally you need to close the dialog. To close a dialog, the recommended method is to call its &lt;code&gt;.Destroy()&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Dialogs do have a &lt;code&gt;.Close()&lt;/code&gt; method, but that basically just hides the dialog, and it will not destroy itself when you close your application, which can lead to weird issues such as your application now shutting down properly. It&amp;rsquo;s simpler to call &lt;code&gt;.Destroy()&lt;/code&gt; on the dialog to prevent this issue.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s update your &lt;code&gt;Mp3Panel&lt;/code&gt; class. You can start by updating &lt;code&gt;.update_mp3_listing()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_folder_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClearAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;140&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertColumn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Year&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;mp3s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;folder_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;/*.mp3&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mp3_objects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eyed3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InsertItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3_objects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3_object&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you set the current directory to the specified folder and then you clear the list control. This keeps the list control fresh and only showing the MP3s that you are currently working on. That also means that you need to re-insert all the columns again.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll want to take the folder that was passed in and use Python&amp;rsquo;s &lt;code&gt;glob&lt;/code&gt; &lt;a href=&quot;https://docs.python.org/3/library/glob.html&quot;&gt;module&lt;/a&gt; to search for MP3 files.&lt;/p&gt;
&lt;p&gt;Then you can loop over the MP3s and turn them into &lt;code&gt;eyed3&lt;/code&gt; objects. You can do this by calling the &lt;code&gt;.load()&lt;/code&gt; of &lt;code&gt;eyed3&lt;/code&gt;. Assuming that the MP3s have the appropriate tags already, you can then add the artist, album, and title of the MP3 to the list control. &lt;/p&gt;
&lt;p&gt;Interestingly, the method of adding a new row to a list control object is by calling &lt;code&gt;.InsertItem()&lt;/code&gt; for the first column and &lt;code&gt;SetItem()&lt;/code&gt; for all the subsequent columns. &lt;/p&gt;
&lt;p&gt;The last step is to save off your MP3 object to your Python dictionary, &lt;code&gt;row_obj_dict&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now you need to update the &lt;code&gt;.on_edit()&lt;/code&gt; event handler so that you can edit an MP3&amp;rsquo;s tags:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_edit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFocusedItem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_obj_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;selection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EditDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ShowModal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_mp3_listing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_folder_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;dlg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first thing you need to do is get the user&amp;rsquo;s selection by calling the list control&amp;rsquo;s &lt;code&gt;.GetFocusedItem()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the user has not selected anything in the list control, it will return &lt;code&gt;-1&lt;/code&gt;. Assuming that the user did select something, you will want to extract the MP3 object from your dictionary and open a MP3 tag editor dialog. This will be a custom dialog that you will use to edit the artist, album, and title tags of the MP3 file. &lt;/p&gt;
&lt;p&gt;As usual, show the dialog modally. When the dialog closes, the last two lines in &lt;code&gt;.on_edit()&lt;/code&gt; will execute. These two lines will update the list control so it displays the current MP3 tag information that the user just edited and destroy the dialog.&lt;/p&gt;
&lt;h4 id=&quot;creating-an-editing-dialog&quot;&gt;Creating an Editing Dialog&lt;/h4&gt;
&lt;p&gt;The final piece of the puzzle is creating an MP3 tag editing dialog. For brevity, we will skip sketching out this interface as it is a series of rows that contains labels and text controls. The text controls should have the existing tag information pre-populated within them. You can create a label for the text controls by creating instances of &lt;code&gt;wx.StaticText&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When you need to create a custom dialog, the &lt;code&gt;wx.Dialog&lt;/code&gt; class is your friend. You can use that to design the editor:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EditDialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dialog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Editing &amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{mp3.tag.title}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;quot;&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;fm&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;VERTICAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Artist&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Album&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextCtrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Save&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EVT_BUTTON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;on_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save_btn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID_CANCEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;btn_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CENTER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;        
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you want to start off by sub-classing &lt;code&gt;wx.Dialog&lt;/code&gt; and giving it a custom title based on the title of the MP3 that you are editing.&lt;/p&gt;
&lt;p&gt;Next you can create the sizer you want to use and the widgets. To make things easier, you can create a helper method called &lt;code&gt;.add_widgets()&lt;/code&gt; for adding the &lt;code&gt;wx.StaticText&lt;/code&gt; widgets as rows with the text control instances. The only other widget here is the &lt;em&gt;Save&lt;/em&gt; button. &lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s write the &lt;code&gt;add_widgets&lt;/code&gt; method next:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BoxSizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HORIZONTAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StaticText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                              &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_ctrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ALL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_sizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_sizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EXPAND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;add_widgets()&lt;/code&gt; takes the label&amp;rsquo;s text and the text control instance. It then creates a horizontally oriented &lt;code&gt;BoxSizer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next you will create an instance of &lt;code&gt;wx.StaticText&lt;/code&gt; using the passed-in text for its label parameter. You will also set its size to be &lt;code&gt;50&lt;/code&gt; pixels wide and the default height is set with a &lt;code&gt;-1&lt;/code&gt;. Since you want the label before the text control, you will add the StaticText widget to your BoxSizer first and then add the text control .&lt;/p&gt;
&lt;p&gt;Finally, you want to add the horizontal sizer to the top level vertical sizer. By nesting the sizers in each other, you can design complex applications.&lt;/p&gt;
&lt;p&gt;Now you will need to create the &lt;code&gt;on_save()&lt;/code&gt; event handler so that you can save your changes:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;on_save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;artist&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mp3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here you set the tags to the contents of the text controls and then call the &lt;code&gt;eyed3&lt;/code&gt; object&amp;rsquo;s &lt;code&gt;.save()&lt;/code&gt;. Finally, you call the &lt;code&gt;.Close()&lt;/code&gt; of the dialog. The reason you call .&lt;code&gt;Close()&lt;/code&gt; here instead of &lt;code&gt;.Destroy()&lt;/code&gt; is that you already call &lt;code&gt;.Destroy()&lt;/code&gt; in the &lt;code&gt;.on_edit()&lt;/code&gt; of your panel subclass. &lt;/p&gt;
&lt;p&gt;Now your application is complete!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You learned a lot about wxPython in this article. You became familiar with the basics of creating GUI applications using wxPython. &lt;/p&gt;
&lt;p&gt;You now know more about the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to work with some of wxPython&amp;rsquo;s widgets&lt;/li&gt;
&lt;li&gt;How events work in wxPython&lt;/li&gt;
&lt;li&gt;How absolute positioning compares with sizers&lt;/li&gt;
&lt;li&gt;How to create a skeleton application&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally you learned how to create a working application, an &lt;a href=&quot;https://github.com/realpython/materials/tree/master/build-a-gui-with-wxpython&quot;&gt;MP3 tag editor&lt;/a&gt;. You can use what you learned in this article to continue to enhance this application or perhaps create an amazing application on your own.&lt;/p&gt;
&lt;p&gt;The wxPython GUI toolkit is robust and full of interesting widgets that you can use to build cross-platform applications. You are limited by only your imagination.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;If you would like to learn more about wxPython, you can check out some of the following links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://wxpython.org/&quot;&gt;Official wxPython website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zetcode&amp;rsquo;s &lt;a href=&quot;http://zetcode.com/wxpython/&quot;&gt;wxPython tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.blog.pythonlibrary.org/category/wxpython/&quot;&gt;Mouse Vs Python Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information on what else you can do with Python, you might want to check out &lt;a href=&quot;https://realpython.com/what-can-i-do-with-python/&quot;&gt;What Can I Do with Python?&lt;/a&gt; If you&amp;rsquo;d like to learn more about Python&amp;rsquo;s &lt;code&gt;super()&lt;/code&gt;, then &lt;a href=&quot;https://realpython.com/python-super/&quot;&gt;Supercharge Your Classes With Python super()&lt;/a&gt; may be just right for you.&lt;/p&gt;
&lt;p&gt;You can also &lt;a href=&quot;https://github.com/realpython/materials/tree/master/build-a-gui-with-wxpython&quot;&gt;download the code&lt;/a&gt; for the MP3 tag editor application that you created in this article if you want to study it more in depth.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Understanding the Python Mock Object Library</title>
      <id>https://realpython.com/python-mock-library/</id>
      <link href="https://realpython.com/python-mock-library/"/>
      <updated>2019-03-13T14:00:00+00:00</updated>
      <summary>In this tutorial, you&#39;ll learn how to use the Python mock object library, unittest.mock, to create and use mock objects to improve your tests. Obstacles like complex logic and unpredictable dependencies make writing valuable tests difficult, but unittest.mock can help you overcome these obstacles.</summary>
      <content type="html">
        &lt;p&gt;When you&amp;rsquo;re writing robust code, tests are essential for verifying that your application logic is correct, reliable, and efficient. However, the value of your tests depends on how well they demonstrate these criteria. Obstacles such as complex logic and unpredictable dependencies make writing valuable tests difficult. The Python mock object library, &lt;code&gt;unittest.mock&lt;/code&gt;, can help you overcome these obstacles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;By the end of this article, you&amp;rsquo;ll be able to:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create Python mock objects using &lt;code&gt;Mock&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Assert you&amp;rsquo;re using objects as you intended&lt;/li&gt;
&lt;li&gt;Inspect usage data stored on your Python mocks&lt;/li&gt;
&lt;li&gt;Configure certain aspects of your Python mock objects&lt;/li&gt;
&lt;li&gt;Substitute your mocks for real objects using &lt;code&gt;patch()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Avoid common problems inherent in Python mocking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You&amp;rsquo;ll begin by seeing what mocking is and how it will improve your tests.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-is-mocking&quot;&gt;What Is Mocking?&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Mock_object&quot;&gt;mock object&lt;/a&gt; substitutes and imitates a real object within a &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;testing environment&lt;/a&gt;. It is a versatile and powerful tool for &lt;a href=&quot;https://realpython.com/python-cli-testing/#mocks&quot;&gt;improving the quality of your tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One reason to use Python mock objects is to control your code&amp;rsquo;s behavior during testing.&lt;/p&gt;
&lt;p&gt;For example, if your code makes &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;HTTP requests&lt;/a&gt; to external services, then your tests execute predictably only so far as the services are behaving as you expected. Sometimes, a temporary change in the behavior of these external services can cause intermittent failures within your test suite.&lt;/p&gt;
&lt;p&gt;Because of this, it would be better for you to test your code in a controlled environment. &lt;a href=&quot;https://realpython.com/testing-third-party-apis-with-mocks/&quot;&gt;Replacing the actual request with a mock object&lt;/a&gt; would allow you to simulate external service outages and successful responses in a predictable way.&lt;/p&gt;
&lt;p&gt;Sometimes, it is difficult to test certain areas of your codebase. Such areas include &lt;code&gt;except&lt;/code&gt; blocks and &lt;code&gt;if&lt;/code&gt; statements that are hard to satisfy. Using Python mock objects can help you control the execution path of your code to reach these areas and improve your &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_coverage&quot;&gt;code coverage&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another reason to use mock objects is to better understand how you&amp;rsquo;re using their real counterparts in your code. A Python mock object contains data about its usage that you can inspect such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you called a method&lt;/li&gt;
&lt;li&gt;How you called the method&lt;/li&gt;
&lt;li&gt;How often you called the method&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding what a mock object does is the first step to learning how to use one. &lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;ll see how to use Python mock objects.&lt;/p&gt;
&lt;h2 id=&quot;the-python-mock-library&quot;&gt;The Python Mock Library&lt;/h2&gt;
&lt;p&gt;The Python &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html&quot;&gt;mock object library&lt;/a&gt; is &lt;code&gt;unittest.mock&lt;/code&gt;. It provides an easy way to introduce mocks into your tests.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The standard library includes &lt;code&gt;unittest.mock&lt;/code&gt; in Python 3.3 and later. If you&amp;rsquo;re using an older version of Python, you&amp;rsquo;ll need to install the official backport of the library. To do so, install &lt;code&gt;mock&lt;/code&gt; from &lt;a href=&quot;https://pypi.org/project/mock/&quot;&gt;PyPI&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install mock
&lt;/pre&gt;&lt;/div&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; provides a class called &lt;code&gt;Mock&lt;/code&gt; which you will use to imitate real objects in your codebase. &lt;code&gt;Mock&lt;/code&gt; offers incredible flexibility and insightful data. This, along with its subclasses, will meet most Python mocking needs that you will face in your tests.&lt;/p&gt;
&lt;p&gt;The library also provides a function, called &lt;code&gt;patch()&lt;/code&gt;, which replaces the real objects in your code with &lt;code&gt;Mock&lt;/code&gt; instances. You can use &lt;code&gt;patch()&lt;/code&gt; as either a decorator or a context manager, giving you control over the scope in which the object will be mocked. Once the designated scope exits, &lt;code&gt;patch()&lt;/code&gt; will clean up your code by replacing the mocked objects with their original counterparts.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;unittest.mock&lt;/code&gt; provides solutions for some of the issues inherent in mocking objects.&lt;/p&gt;
&lt;p&gt;Now, you have a better understanding of what mocking is and the library you&amp;rsquo;ll be using to do it. Let&amp;rsquo;s dive in and explore what features and functionalities &lt;code&gt;unittest.mock&lt;/code&gt; offers.&lt;/p&gt;
&lt;h2 id=&quot;the-mock-object&quot;&gt;The &lt;code&gt;Mock&lt;/code&gt; Object&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; offers a base class for mocking objects called &lt;code&gt;Mock&lt;/code&gt;. The use cases for &lt;code&gt;Mock&lt;/code&gt; are practically limitless because &lt;code&gt;Mock&lt;/code&gt; is so flexible.&lt;/p&gt;
&lt;p&gt;Begin by instantiating a new &lt;code&gt;Mock&lt;/code&gt; instance:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock id=&amp;#39;4561344720&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you are able to substitute an object in your code with your new &lt;code&gt;Mock&lt;/code&gt;. You can do this by passing it as an argument to a function or by redefining another object:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Pass mock as an argument to do_something()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Patch the json library&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When you substitute an object in your code, the &lt;code&gt;Mock&lt;/code&gt; must look like the real object it is replacing. Otherwise, your code will not be able to use the &lt;code&gt;Mock&lt;/code&gt; in place of the original object.&lt;/p&gt;
&lt;p&gt;For example, if you are mocking the &lt;code&gt;json&lt;/code&gt; library and your program calls &lt;code&gt;dumps()&lt;/code&gt;, then your Python mock object must also contain &lt;code&gt;dumps()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll see how &lt;code&gt;Mock&lt;/code&gt; deals with this challenge.&lt;/p&gt;
&lt;h3 id=&quot;lazy-attributes-and-methods&quot;&gt;Lazy Attributes and Methods&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;Mock&lt;/code&gt; must simulate any object that it replaces. To achieve such flexibility, it &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#quick-guide&quot;&gt;creates its attributes when you access them&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some_attribute&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.some_attribute&amp;#39; id=&amp;#39;4394778696&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;do_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.do_something()&amp;#39; id=&amp;#39;4394778920&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;Mock&lt;/code&gt; can create arbitrary attributes on the fly, it is suitable to replace any object.&lt;/p&gt;
&lt;p&gt;Using an example from earlier, if you&amp;rsquo;re mocking the &lt;code&gt;json&lt;/code&gt; library and you call &lt;code&gt;dumps()&lt;/code&gt;, the Python mock object will create the method so that its interface can match the library&amp;rsquo;s interface:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.dumps()&amp;#39; id=&amp;#39;4392249776&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice two key characteristics of this mocked version of &lt;code&gt;dumps()&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Unlike the &lt;a href=&quot;https://realpython.com/python-json/#serializing-json&quot;&gt;real &lt;code&gt;dumps()&lt;/code&gt;&lt;/a&gt;, this mocked method requires no arguments. In fact, it will accept any arguments that you pass to it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The return value of &lt;code&gt;dumps()&lt;/code&gt; is also a &lt;code&gt;Mock&lt;/code&gt;. The capability of &lt;code&gt;Mock&lt;/code&gt; to recursively define other mocks allows for you to use mocks in complex situations:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;k&amp;quot;: &amp;quot;v&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;k&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads().get()&amp;#39; id=&amp;#39;4379599424&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because the return value of each mocked method is also a &lt;code&gt;Mock&lt;/code&gt;, you can use your mocks in a multitude of ways.&lt;/p&gt;
&lt;p&gt;Mocks are flexible, but they&amp;rsquo;re also informative. Next, you&amp;rsquo;ll learn how you can use mocks to understand your code better.&lt;/p&gt;
&lt;h3 id=&quot;assertions-and-inspection&quot;&gt;Assertions and Inspection&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Mock&lt;/code&gt; instances store data on how you used them. For instance, you can see if you called a method, how you called the method, and so on. There are two main ways to use this information.&lt;/p&gt;
&lt;p&gt;First, you can assert that your program used an object as you expected:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Create a mock object&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4550144184&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# You know that you called loads() so you can&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# make assertions to test that expectation&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4550144184&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# If an assertion fails, the mock will raise an AssertionError&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;795&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to have been called once. Called 2 times.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;824&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_once_with&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to be called once. Called 2 times.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_not_called&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;777&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_not_called&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected &amp;#39;loads&amp;#39; to not have been called. Called 2 times.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.assert_called()&lt;/code&gt; ensures you called the mocked method while &lt;code&gt;.assert_called_once()&lt;/code&gt; checks that you called the method exactly one time.&lt;/p&gt;
&lt;p&gt;Both assertion functions have variants that let you inspect the arguments passed to the mocked method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.assert_called_with(*args, **kwargs)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.assert_called_once_with(*args, **kwargs)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To pass these assertions, you must call the mocked method with the same arguments that you pass to the actual method:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;814&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AssertionError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_error_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cause&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AssertionError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Expected call: loads(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Actual call: loads(s=&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;json.loads.assert_called_with(&#39;{&quot;key&quot;: &quot;value&quot;}&#39;)&lt;/code&gt; raised an &lt;code&gt;AssertionError&lt;/code&gt; because it expected you to call &lt;a href=&quot;https://realpython.com/python-json/#deserializing-json&quot;&gt;&lt;code&gt;loads()&lt;/code&gt;&lt;/a&gt; with a positional argument, but you actually called it with a keyword argument. &lt;code&gt;json.loads.assert_called_with(s=&#39;{&quot;key&quot;: &quot;value&quot;}&#39;)&lt;/code&gt; gets this assertion correct.&lt;/p&gt;
&lt;p&gt;Second, you can view special attributes to understand how your application used an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Create a mock object&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.loads()&amp;#39; id=&amp;#39;4391026640&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Number of times you called loads():&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_count&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# The last loads() call:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_args&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;call(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# List of loads() calls:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_args_list&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[call(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)]&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# List of calls to json&amp;#39;s methods (recursively):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;method_calls&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[call.loads(&amp;#39;{&amp;quot;key&amp;quot;: &amp;quot;value&amp;quot;}&amp;#39;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can write tests using these attributes to make sure that your objects behave as you intended.&lt;/p&gt;
&lt;p&gt;Now, you can create mocks and inspect their usage data. Next, you&amp;rsquo;ll see how to customize mocked methods so that they become more useful in your testing environment.&lt;/p&gt;
&lt;h3 id=&quot;managing-a-mocks-return-value&quot;&gt;Managing a Mock&amp;rsquo;s Return Value&lt;/h3&gt;
&lt;p&gt;One reason to use mocks is to control your code&amp;rsquo;s behavior during tests. One way to do this is to specify a function&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.return_value&quot;&gt;return value&lt;/a&gt;. Let&amp;rsquo;s use an example to see how this works.&lt;/p&gt;
&lt;p&gt;First, create a file called &lt;code&gt;my_calendar.py&lt;/code&gt;. Add &lt;code&gt;is_weekday()&lt;/code&gt;, a function that uses Python&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/2/library/datetime.html&quot;&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/a&gt; library to determine whether or not today is a week day. Finally, write a test that asserts that the function works as expected:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Test if today is a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Since you&amp;rsquo;re testing if today is a weekday, the result depends on the day you run your test:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If this command produces no output, the assertion was successful. Unfortunately, if you run the command on a weekend, you&amp;rsquo;ll get an &lt;code&gt;AssertionError&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  File &amp;quot;test.py&amp;quot;, line 9, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    assert is_weekday()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;AssertionError&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When writing tests, it is important to ensure that the results are predictable. You can use &lt;code&gt;Mock&lt;/code&gt; to eliminate uncertainty from your code during testing. In this case, you can mock &lt;code&gt;datetime&lt;/code&gt; and set the &lt;code&gt;.return_value&lt;/code&gt; for &lt;code&gt;.today()&lt;/code&gt; to a day that you choose:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Save a couple of test days&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tuesday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;saturday&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2019&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock datetime to control today&amp;#39;s date&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock .today() to return Tuesday&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tuesday&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Test Tuesday is a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Mock .today() to return Saturday&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;saturday&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Test Saturday is not a weekday&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the example, &lt;code&gt;.today()&lt;/code&gt; is a mocked method. You&amp;rsquo;ve removed the inconsistency by assigning a specific day to the mock&amp;rsquo;s &lt;code&gt;.return_value&lt;/code&gt;. That way, when you call &lt;code&gt;.today()&lt;/code&gt;, it returns the &lt;code&gt;datetime&lt;/code&gt; that you specified.&lt;/p&gt;
&lt;p&gt;In the first test, you ensure &lt;code&gt;tuesday&lt;/code&gt; is a weekday. In the second test, you verify that &lt;code&gt;saturday&lt;/code&gt; is not a weekday. Now, it doesn&amp;rsquo;t matter what day you run your tests on because you&amp;rsquo;ve mocked &lt;code&gt;datetime&lt;/code&gt; and have control over the object&amp;rsquo;s behavior.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; Though mocking &lt;code&gt;datetime&lt;/code&gt; like this is a good practice example for using &lt;code&gt;Mock&lt;/code&gt;, a fantastic library already exists for mocking &lt;code&gt;datetime&lt;/code&gt; called &lt;a href=&quot;https://github.com/spulec/freezegun&quot;&gt;&lt;code&gt;freezegun&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;When building your tests, you will likely come across cases where mocking a function&amp;rsquo;s return value will not be enough. This is because functions are often more complicated than a simple one-way flow of logic.&lt;/p&gt;
&lt;p&gt;Sometimes, you&amp;rsquo;ll want to make functions return different values when you call them more than once or even raise exceptions. You can do this using &lt;code&gt;.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;managing-a-mocks-side-effects&quot;&gt;Managing a Mock&amp;rsquo;s Side Effects&lt;/h3&gt;
&lt;p&gt;You can control your code&amp;rsquo;s behavior by specifying a mocked function&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.side_effect&quot;&gt;side effects&lt;/a&gt;. A &lt;code&gt;.side_effect&lt;/code&gt; defines what happens when you call the mocked function.&lt;/p&gt;
&lt;p&gt;To test how this works, add a new function to &lt;code&gt;my_calendar.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;get_holidays()&lt;/code&gt; makes a request to the &lt;code&gt;localhost&lt;/code&gt; server for a set of holidays. If the server responds successfully, &lt;code&gt;get_holidays()&lt;/code&gt; will return a dictionary. Otherwise, the method will return &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can test how &lt;code&gt;get_holidays()&lt;/code&gt; will respond to a connection timeout by setting &lt;code&gt;requests.get.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For this example, you&amp;rsquo;ll only see the relevant code from &lt;code&gt;my_calendar.py&lt;/code&gt;. You&amp;rsquo;ll build a test case using Python&amp;rsquo;s &lt;a href=&quot;https://docs.python.org/3/library/unittest.html&quot;&gt;&lt;code&gt;unittest&lt;/code&gt;&lt;/a&gt; library:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Test a connection timeout&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You use &lt;code&gt;.assertRaises()&lt;/code&gt; to verify that &lt;code&gt;get_holidays()&lt;/code&gt; raises an exception given the new side effect of &lt;code&gt;get()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Run this test to see the result of your test:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.000s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to be a little more dynamic, you can set &lt;code&gt;.side_effect&lt;/code&gt; to a function that &lt;code&gt;Mock&lt;/code&gt; will invoke when you call your mocked method. The mock shares the arguments and return value of the &lt;code&gt;.side_effect&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log_request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Log a fake request for test output purposes&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Making a request to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{url}&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Request received!&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# Create a new Mock to imitate a Response&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Test a successful, logged request&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log_request&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you created &lt;code&gt;.log_request()&lt;/code&gt;, which takes a URL, logs some output using &lt;code&gt;print()&lt;/code&gt;, then returns a &lt;code&gt;Mock&lt;/code&gt; response. Next, you set the &lt;code&gt;.side_effect&lt;/code&gt; of &lt;code&gt;get()&lt;/code&gt; to &lt;code&gt;.log_request()&lt;/code&gt;, which you&amp;rsquo;ll use when you call &lt;code&gt;get_holidays()&lt;/code&gt;. When you run your test, you&amp;rsquo;ll see that &lt;code&gt;get()&lt;/code&gt; forwards its arguments to &lt;code&gt;.log_request()&lt;/code&gt; then accepts the return value and returns it as well:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python my_calendar.py
&lt;span class=&quot;go&quot;&gt;Making a request to http://localhost/api/holidays.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Request received!&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.000s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! The &lt;code&gt;print()&lt;/code&gt; statements logged the correct values. Also, &lt;code&gt;get_holidays()&lt;/code&gt; returned the holidays dictionary.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.side_effect&lt;/code&gt; can also be an iterable. The iterable must consist of return values, exceptions, or a mixture of both. The iterable will produce its next value every time you call your mocked method. For example, you can test that a retry after a &lt;code&gt;Timeout&lt;/code&gt; returns a successful response:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Mock requests to control its behavior&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_retry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Create a new Mock to imitate a Response&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Set the side effect of .get()&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# Test that the first request raises a Timeout&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Now retry, expecting a successful response&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Finally, assert .get() was called twice&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first time you call &lt;code&gt;get_holidays()&lt;/code&gt;, &lt;code&gt;get()&lt;/code&gt; raises a &lt;code&gt;Timeout&lt;/code&gt;. The second time, the method returns a valid holidays dictionary. These side effects match the order they appear in the list passed to &lt;code&gt;.side_effect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can set &lt;code&gt;.return_value&lt;/code&gt; and &lt;code&gt;.side_effect&lt;/code&gt; on a &lt;code&gt;Mock&lt;/code&gt; directly. However, because a Python mock object needs to be flexible in creating its attributes, there is a better way to configure these and other settings.&lt;/p&gt;
&lt;h3 id=&quot;configuring-your-mock&quot;&gt;Configuring Your Mock&lt;/h3&gt;
&lt;p&gt;You can configure a &lt;code&gt;Mock&lt;/code&gt; to set up some of the object&amp;rsquo;s behaviors. Some configurable members include &lt;code&gt;.side_effect&lt;/code&gt;, &lt;code&gt;.return_value&lt;/code&gt;, and &lt;code&gt;.name&lt;/code&gt;. You configure a &lt;code&gt;Mock&lt;/code&gt; when you &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock&quot;&gt;create&lt;/a&gt; one or when you use &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.configure_mock&quot;&gt;&lt;code&gt;.configure_mock()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can configure a &lt;code&gt;Mock&lt;/code&gt; by specifying certain attributes when you initialize an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;ne&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;939&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__call__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_mock_self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_mock_call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;995&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;_mock_call&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;effect&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;Exception&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;Real Python Mock&amp;#39; id=&amp;#39;4434041432&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While &lt;code&gt;.side_effect&lt;/code&gt; and &lt;code&gt;.return_value&lt;/code&gt; can be set on the &lt;code&gt;Mock&lt;/code&gt; instance, itself, other attributes like &lt;code&gt;.name&lt;/code&gt; can only be set through &lt;code&gt;.__init__()&lt;/code&gt; or &lt;code&gt;.configure_mock()&lt;/code&gt;. If you try to set the &lt;code&gt;.name&lt;/code&gt; of the &lt;code&gt;Mock&lt;/code&gt; on the instance, you will get a different result:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;Real Python Mock.name&amp;#39; id=&amp;#39;4434041544&amp;#39;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;#39;Real Python Mock&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.name&lt;/code&gt; is a common attribute for objects to use. So, &lt;code&gt;Mock&lt;/code&gt; doesn&amp;rsquo;t let you set that value on the instance in the same way you can with &lt;code&gt;.return_value&lt;/code&gt; or &lt;code&gt;.side_effect&lt;/code&gt;. If you access &lt;code&gt;mock.name&lt;/code&gt; you will create a &lt;code&gt;.name&lt;/code&gt; attribute instead of configuring your mock.&lt;/p&gt;
&lt;p&gt;You can configure an existing &lt;code&gt;Mock&lt;/code&gt; using &lt;code&gt;.configure_mock()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure_mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By unpacking a dictionary into either &lt;code&gt;.configure_mock()&lt;/code&gt; or &lt;code&gt;Mock.__init__()&lt;/code&gt;, you can even configure your Python mock object&amp;rsquo;s attributes. Using &lt;code&gt;Mock&lt;/code&gt; configurations, you could simplify a previous example:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Verbose, old Mock&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;return_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Shiny, new .configure_mock()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;holidays&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;12/25&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Christmas&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;7/4&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Independence Day&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response_mock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;json.return_value&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you can create and configure Python mock objects. You can also use mocks to control the behavior of your application. So far, you&amp;rsquo;ve used mocks as arguments to functions or patching objects in the same module as your tests.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll learn how to substitute your mocks for real objects in other modules.&lt;/p&gt;
&lt;h2 id=&quot;patch&quot;&gt;&lt;code&gt;patch()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;unittest.mock&lt;/code&gt; provides a powerful mechanism for mocking objects, called &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#patch&quot;&gt;&lt;code&gt;patch()&lt;/code&gt;&lt;/a&gt;, which looks up an object in a given module and replaces that object with a &lt;code&gt;Mock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Usually, you use &lt;code&gt;patch()&lt;/code&gt; as a decorator or a context manager to provide a scope in which you will mock the target object.&lt;/p&gt;
&lt;h3 id=&quot;patch-as-a-decorator&quot;&gt;&lt;code&gt;patch()&lt;/code&gt; as a Decorator&lt;/h3&gt;
&lt;p&gt;If you want to mock an object for the duration of your entire test function, you can use &lt;code&gt;patch()&lt;/code&gt; as a function &lt;a href=&quot;https://realpython.com/primer-on-python-decorators/&quot;&gt;decorator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To see how this works, reorganize your &lt;code&gt;my_calendar.py&lt;/code&gt; file by putting the logic and tests into separate files:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Python&amp;#39;s datetime library treats Monday as 0 and Sunday as 6&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;today&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;http://localhost/api/holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These functions are now in their own file, separate from their tests. Next, you&amp;rsquo;ll re-create your tests in a file called &lt;code&gt;tests.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Up to this point, you&amp;rsquo;ve monkey patched objects in the file in which they exist. &lt;a href=&quot;https://en.wikipedia.org/wiki/Monkey_patch&quot;&gt;Monkey patching&lt;/a&gt; is the replacement of one object with another at runtime. Now, you&amp;rsquo;ll use &lt;code&gt;patch()&lt;/code&gt; to replace your objects in &lt;code&gt;my_calendar.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;nd&quot;&gt;@patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.requests&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Originally, you created a &lt;code&gt;Mock&lt;/code&gt; and patched &lt;code&gt;requests&lt;/code&gt; in the local scope. Now, you need to access the &lt;code&gt;requests&lt;/code&gt; library in &lt;code&gt;my_calendar.py&lt;/code&gt; from &lt;code&gt;tests.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For this case, you used &lt;code&gt;patch()&lt;/code&gt; as a decorator and passed the target object&amp;rsquo;s path. The target path was &lt;code&gt;&#39;my_calendar.requests&#39;&lt;/code&gt; which consists of the module name and the object.&lt;/p&gt;
&lt;p&gt;You also defined a new parameter for the test function. &lt;code&gt;patch()&lt;/code&gt; uses this parameter to pass the mocked object into your test. From there, you can modify the mock or make assertions as necessary.&lt;/p&gt;
&lt;p&gt;You can execute this test module to ensure it&amp;rsquo;s working as expected:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python tests.py
&lt;span class=&quot;go&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Ran 1 test in 0.001s&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;OK&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Technical Detail:&lt;/strong&gt; &lt;code&gt;patch()&lt;/code&gt; returns an instance of &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock&quot;&gt;&lt;code&gt;MagicMock&lt;/code&gt;&lt;/a&gt;, which is a &lt;code&gt;Mock&lt;/code&gt; subclass. &lt;code&gt;MagicMock&lt;/code&gt; is useful because it implements most &lt;a href=&quot;https://dbader.org/blog/python-dunder-methods&quot;&gt;magic methods&lt;/a&gt; for you, such as &lt;code&gt;.__len__()&lt;/code&gt;, &lt;code&gt;.__str__()&lt;/code&gt;, and &lt;code&gt;.__iter__()&lt;/code&gt;, with reasonable defaults.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;patch()&lt;/code&gt; as a decorator worked well in this example. In some cases, it is more readable, more effective, or easier to use &lt;code&gt;patch()&lt;/code&gt; as a context manager.&lt;/p&gt;
&lt;h3 id=&quot;patch-as-a-context-manager&quot;&gt;&lt;code&gt;patch()&lt;/code&gt; as a Context Manager&lt;/h3&gt;
&lt;p&gt;Sometimes, you&amp;rsquo;ll want to use &lt;code&gt;patch()&lt;/code&gt; as a &lt;a href=&quot;https://dbader.org/blog/python-context-managers-and-with-statement&quot;&gt;context manager&lt;/a&gt; rather than a decorator. Some reasons why you might prefer a context manager include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You only want to mock an object for a part of the test scope.&lt;/li&gt;
&lt;li&gt;You are already using too many decorators or parameters, which hurts your test&amp;rsquo;s readability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To use &lt;code&gt;patch()&lt;/code&gt; as a context manager, you use Python&amp;rsquo;s &lt;code&gt;with&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests.exceptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.requests&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assert_called_once&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the test exits the &lt;code&gt;with&lt;/code&gt; statement, &lt;code&gt;patch()&lt;/code&gt; replaces the mocked object with the original.&lt;/p&gt;
&lt;p&gt;Until now, you&amp;rsquo;ve mocked complete objects, but sometimes you&amp;rsquo;ll only want to mock a part of an object.&lt;/p&gt;
&lt;h3 id=&quot;patching-an-objects-attributes&quot;&gt;Patching an Object&amp;rsquo;s Attributes&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s say you only want to mock one method of an object instead of the entire object. You can do so by using &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#patch-object&quot;&gt;&lt;code&gt;patch.object()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;.test_get_holidays_timeout()&lt;/code&gt; really only needs to mock &lt;code&gt;requests.get()&lt;/code&gt; and set its &lt;code&gt;.side_effect&lt;/code&gt; to &lt;code&gt;Timeout&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TestCalendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TestCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;nd&quot;&gt;@patch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;get&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;side_effect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_get_holidays_timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertRaises&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exceptions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;get_holidays&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;unittest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, you&amp;rsquo;ve mocked only &lt;code&gt;get()&lt;/code&gt; rather than all of &lt;code&gt;requests&lt;/code&gt;. Every other attribute remains the same.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;object()&lt;/code&gt; takes the same configuration parameters that &lt;code&gt;patch()&lt;/code&gt; does. But instead of passing the target&amp;rsquo;s path, you provide the target object, itself, as the first parameter. The second parameter is the attribute of the target object that you are trying to mock. You can also use &lt;code&gt;object()&lt;/code&gt; as a context manager like &lt;code&gt;patch()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Further Reading:&lt;/strong&gt; Besides objects and attributes, you can also &lt;code&gt;patch()&lt;/code&gt; dictionaries with &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.dict&quot;&gt;&lt;code&gt;patch.dict()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Learning how to use &lt;code&gt;patch()&lt;/code&gt; is critical to mocking objects in other modules. However, sometimes it&amp;rsquo;s not obvious what the target object&amp;rsquo;s path is.&lt;/p&gt;
&lt;h3 id=&quot;where-to-patch&quot;&gt;Where to Patch&lt;/h3&gt;
&lt;p&gt;Knowing where to tell &lt;code&gt;patch()&lt;/code&gt; to look for the object you want mocked is important because if you choose the wrong target location, the result of &lt;code&gt;patch()&lt;/code&gt; could be something you didn&amp;rsquo;t expect.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you are mocking &lt;code&gt;is_weekday()&lt;/code&gt; in &lt;code&gt;my_calendar.py&lt;/code&gt; using &lt;code&gt;patch()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;is_weekday()&amp;#39; id=&amp;#39;4336501256&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;First, you import &lt;code&gt;my_calendar.py&lt;/code&gt;. Then you patch &lt;code&gt;is_weekday()&lt;/code&gt;, replacing it with a &lt;code&gt;Mock&lt;/code&gt;. Great! This is working as expected.&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s change this example slightly and import the function directly:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;my_calendar.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Depending on what day you are reading this tutorial, your console output may read &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;. The important thing is that the output is not a &lt;code&gt;Mock&lt;/code&gt; like before.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Notice that even though the target location you passed to &lt;code&gt;patch()&lt;/code&gt; did not change, the result of calling &lt;code&gt;is_weekday()&lt;/code&gt; is different. The difference is due to the change in how you imported the function.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;from my_calendar import is_weekday&lt;/code&gt; binds the real function to the local scope. So, even though you &lt;code&gt;patch()&lt;/code&gt; the function later, you ignore the mock because you already have a local reference to the un-mocked function.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html#where-to-patch&quot;&gt;good rule of thumb&lt;/a&gt; is to &lt;code&gt;patch()&lt;/code&gt; the object where it is looked up.&lt;/p&gt;
&lt;p&gt;In the first example, mocking &lt;code&gt;&#39;my_calendar.is_weekday()&#39;&lt;/code&gt; works because you look up the function in the &lt;code&gt;my_calendar&lt;/code&gt; module. In the second example, you have a local reference to &lt;code&gt;is_weekday()&lt;/code&gt;. Since you use the function found in the local scope, you should mock the local function:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__.is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;is_weekday()&amp;#39; id=&amp;#39;4502362992&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you have a firm grasp on the power of &lt;code&gt;patch()&lt;/code&gt;. You&amp;rsquo;ve seen how to &lt;code&gt;patch()&lt;/code&gt; objects and attributes as well as where to patch them.&lt;/p&gt;
&lt;p&gt;Next, you&amp;rsquo;ll see some common problems inherent in object mocking and the solutions that &lt;code&gt;unittest.mock&lt;/code&gt; provides.&lt;/p&gt;
&lt;h2 id=&quot;common-mocking-problems&quot;&gt;Common Mocking Problems&lt;/h2&gt;
&lt;p&gt;Mocking objects can introduce several problems into your tests. Some problems are inherent in mocking while others are specific to &lt;code&gt;unittest.mock&lt;/code&gt;. Keep in mind that there are other issues with mocking that are not mentioned in this tutorial.&lt;/p&gt;
&lt;p&gt;The ones covered here are similar to each other in that the problem they cause is fundamentally the same. In each case, the test assertions are irrelevant. Though the intention of each mock is valid, the mocks themselves are not.&lt;/p&gt;
&lt;h3 id=&quot;changes-to-object-interfaces-and-misspellings&quot;&gt;Changes to Object Interfaces and Misspellings&lt;/h3&gt;
&lt;p&gt;Classes and function definitions change all the time. When the interface of an object changes, any tests relying on a &lt;code&gt;Mock&lt;/code&gt; of that object may become irrelevant.&lt;/p&gt;
&lt;p&gt;For example, you rename a method but forget that a test mocks that method and invokes &lt;code&gt;.assert_not_called()&lt;/code&gt;. After the change, &lt;code&gt;.assert_not_called()&lt;/code&gt; is still &lt;code&gt;True&lt;/code&gt;. The assertion is not useful, though, because the method no longer exists.&lt;/p&gt;
&lt;p&gt;Irrelevant tests may not sound critical, but if they are your only tests and you assume that they work properly, the situation could be disastrous for your application.&lt;/p&gt;
&lt;p&gt;A problem specific to &lt;code&gt;Mock&lt;/code&gt; is that a misspelling can break a test. Recall that a &lt;code&gt;Mock&lt;/code&gt; creates its interface when you access its members. So, you will inadvertently create a new attribute if you misspell its name.&lt;/p&gt;
&lt;p&gt;If you call &lt;code&gt;.asert_called()&lt;/code&gt; instead of &lt;code&gt;.assert_called()&lt;/code&gt;, your test will not raise an &lt;code&gt;AssertionError&lt;/code&gt;. This is because you&amp;rsquo;ve created a new method on the Python mock object named &lt;code&gt;.asert_called()&lt;/code&gt; instead of evaluating an actual assertion.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Technical Detail:&lt;/strong&gt; Interestingly, &lt;code&gt;assret&lt;/code&gt; is a special misspelling of &lt;code&gt;assert&lt;/code&gt;. If you try to access an attribute that starts with &lt;code&gt;assret&lt;/code&gt; (or &lt;code&gt;assert&lt;/code&gt;), &lt;code&gt;Mock&lt;/code&gt; will automatically raise an &lt;code&gt;AttributeError&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;These problems occur when you mock objects within your own codebase. A different problem arises when you mock objects interacting with external codebases.&lt;/p&gt;
&lt;h3 id=&quot;changes-to-external-dependencies&quot;&gt;Changes to External Dependencies&lt;/h3&gt;
&lt;p&gt;Imagine again that your code makes a request to an external API. In this case, the external dependency is the API which is susceptible to change without your consent.&lt;/p&gt;
&lt;p&gt;On one hand, unit tests test isolated components of code. So, mocking the code that makes the request helps you to test your isolated components under controlled conditions. However, it also presents a potential problem.&lt;/p&gt;
&lt;p&gt;If an external dependency changes its interface, your Python mock objects will become invalid. If this happens (and the interface change is a breaking one), your tests will pass because your mock objects have masked the change, but your production code will fail.&lt;/p&gt;
&lt;p&gt;Unfortunately, this is not a problem that &lt;code&gt;unittest.mock&lt;/code&gt; provides a solution for. You must exercise judgment when mocking external dependencies.&lt;/p&gt;
&lt;p&gt;All three of these issues can cause test irrelevancy and potentially costly issues because they threaten the integrity of your mocks. &lt;code&gt;unittest.mock&lt;/code&gt; gives you some tools for dealing with these problems.&lt;/p&gt;
&lt;h2 id=&quot;avoiding-common-problems-using-specifications&quot;&gt;Avoiding Common Problems Using Specifications&lt;/h2&gt;
&lt;p&gt;As mentioned before, if you change a class or function definition or you misspell a Python mock object&amp;rsquo;s attribute, you can cause problems with your tests.&lt;/p&gt;
&lt;p&gt;These problems occur because &lt;code&gt;Mock&lt;/code&gt; creates attributes and methods when you access them. The answer to these issues is to prevent &lt;code&gt;Mock&lt;/code&gt; from creating attributes that don&amp;rsquo;t conform to the object you&amp;rsquo;re trying to mock.&lt;/p&gt;
&lt;p&gt;When configuring a &lt;code&gt;Mock&lt;/code&gt;, you can pass an object specification to the &lt;code&gt;spec&lt;/code&gt; parameter. The &lt;code&gt;spec&lt;/code&gt; parameter accepts a list of names or another object and defines the mock&amp;rsquo;s interface. If you attempt to access an attribute that does not belong to the specification, &lt;code&gt;Mock&lt;/code&gt; will raise an &lt;code&gt;AttributeError&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;is_weekday&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;get_holidays&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4569015856&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, you&amp;rsquo;ve specified that &lt;code&gt;calendar&lt;/code&gt; has methods called &lt;code&gt;.is_weekday()&lt;/code&gt; and &lt;code&gt;.get_holidays()&lt;/code&gt;. When you access &lt;code&gt;.is_weekday()&lt;/code&gt;, it returns a &lt;code&gt;Mock&lt;/code&gt;. When you access &lt;code&gt;.create_event()&lt;/code&gt;, a method that does not match the specification, &lt;code&gt;Mock&lt;/code&gt; raises an &lt;code&gt;AttributeError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Specifications work the same way if you configure the &lt;code&gt;Mock&lt;/code&gt; with an object:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;Mock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4569435216&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;.is_weekday()&lt;/code&gt; is available to &lt;code&gt;calendar&lt;/code&gt; because you configured &lt;code&gt;calendar&lt;/code&gt; to match the &lt;code&gt;my_calendar&lt;/code&gt; module&amp;rsquo;s interface.&lt;/p&gt;
&lt;p&gt;Furthermore, &lt;code&gt;unittest.mock&lt;/code&gt; provides convenient methods of automatically specifying a &lt;code&gt;Mock&lt;/code&gt; instance&amp;rsquo;s interface.&lt;/p&gt;
&lt;p&gt;One way to implement automatic specifications is &lt;code&gt;create_autospec&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_autospec&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_autospec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;mock.is_weekday()&amp;#39; id=&amp;#39;4579049424&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Like before, &lt;code&gt;calendar&lt;/code&gt; is a &lt;code&gt;Mock&lt;/code&gt; instance whose interface matches &lt;code&gt;my_calendar&lt;/code&gt;. If you&amp;rsquo;re using &lt;code&gt;patch()&lt;/code&gt;, you can send an argument to the &lt;code&gt;autospec&lt;/code&gt; parameter to achieve the same result:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;my_calendar&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;unittest.mock&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__.my_calendar&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;autospec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_weekday&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;... &lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;calendar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;&amp;lt;MagicMock name=&amp;#39;my_calendar.is_weekday()&amp;#39; id=&amp;#39;4579094312&amp;#39;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;gt&quot;&gt;Traceback (most recent call last):&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
  File &lt;span class=&quot;nb&quot;&gt;&amp;quot;/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py&amp;quot;&lt;/span&gt;, line &lt;span class=&quot;m&quot;&gt;582&lt;/span&gt;, in &lt;span class=&quot;n&quot;&gt;__getattr__&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;AttributeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Mock object has no attribute &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%r&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;gr&quot;&gt;AttributeError&lt;/span&gt;: &lt;span class=&quot;n&quot;&gt;Mock object has no attribute &amp;#39;create_event&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve learned so much about mocking objects using &lt;code&gt;unittest.mock&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Now, you&amp;rsquo;re able to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Mock&lt;/code&gt; to imitate objects in your tests&lt;/li&gt;
&lt;li&gt;Check usage data to understand how you use your objects&lt;/li&gt;
&lt;li&gt;Customize your mock objects&amp;rsquo; return values and side effects&lt;/li&gt;
&lt;li&gt;&lt;code&gt;patch()&lt;/code&gt; objects throughout your codebase&lt;/li&gt;
&lt;li&gt;See and avoid problems with using Python mock objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You have built a foundation of understanding that will help you build better tests. You can use mocks to gain insights into your code that you would not have been able to get otherwise.&lt;/p&gt;
&lt;p&gt;I leave you with one final disclaimer. &lt;em&gt;Beware of overusing mock objects!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easy to take advantage of the power of Python mock objects and mock so much that you actually decrease the value of your tests.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in learning more about &lt;code&gt;unittest.mock&lt;/code&gt;, I encourage you to read its excellent &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Managing Multiple Python Versions With pyenv</title>
      <id>https://realpython.com/intro-to-pyenv/</id>
      <link href="https://realpython.com/intro-to-pyenv/"/>
      <updated>2019-03-11T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to install multiple Python versions and switch between them with ease, including project-specific virtual environments, even if you don&#39;t have sudo access with pyenv.</summary>
      <content type="html">
        &lt;p&gt;Have you ever wanted to contribute to a project that supports multiple versions of Python but aren&amp;rsquo;t sure how you would easily &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;test&lt;/a&gt; all the versions? Are you ever curious about the latest and greatest versions of Python? Maybe you&amp;rsquo;d like to try out these new features, but you don&amp;rsquo;t want to worry about messing up your development environment. Luckily, managing multiple version of Python doesn&amp;rsquo;t have to be confusing if you use &lt;code&gt;pyenv&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;This article will provide you with a great overview of how to maximize your time spent working on projects and minimize the time spent in headaches trying to use the right version of Python.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this article, you&amp;rsquo;ll learn how to:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Install the latest development version of Python&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;li&gt;Use virtual environments with &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Activate different Python versions and virtual environments automatically&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-tricks-sample&quot; data-focus=&quot;false&quot;&gt;Click here to get access to a chapter from Python Tricks: The Book&lt;/a&gt; that shows you Python&#39;s best practices with simple examples you can apply instantly to write more beautiful + Pythonic code.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;why-use-pyenv&quot;&gt;Why Use &lt;code&gt;pyenv&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; is a wonderful tool for managing multiple Python versions. Even if you already have Python installed on your system, it is worth having &lt;code&gt;pyenv&lt;/code&gt; installed so that you can easily try out new language features or help contribute to a project that is on a different version of Python.&lt;/p&gt;
&lt;h3 id=&quot;why-not-use-system-python&quot;&gt;Why Not Use System Python?&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;System Python&amp;rdquo; is the Python that comes installed on your operating system. If you&amp;rsquo;re on Mac or Linux, then by default, when you type &lt;code&gt;python&lt;/code&gt; in your terminal, you get a nice Python REPL. &lt;/p&gt;
&lt;p&gt;So, why not use it? One way to look at it is that this Python really &lt;em&gt;belongs&lt;/em&gt; to the operating system. After all, it came installed with the operating system. That&amp;rsquo;s even reflected when you run &lt;code&gt;which&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;python&lt;/code&gt; is available to all users as evidenced by its location &lt;code&gt;/usr/bin/python&lt;/code&gt;. Chances are, this isn&amp;rsquo;t the version of Python you want either:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Pyhton 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To install a package into your system Python, you have to run &lt;code&gt;sudo pip install&lt;/code&gt;. That&amp;rsquo;s because you&amp;rsquo;re installing the Python package globally, which is a real problem if another user comes along and wants to install a slightly older version of the package.&lt;/p&gt;
&lt;p&gt;Problems with multiple versions of the same package tend to creep up on you and bite you when you least expect it. One common way this problem presents itself is a popular and stable package suddenly misbehaving on your system. After hours of troubleshooting and Googling, you may find that you&amp;rsquo;ve installed the wrong version of a dependency, and it&amp;rsquo;s ruining your day.&lt;/p&gt;
&lt;p&gt;Even if your Python version is installed in &lt;code&gt;/usr/local/bin/python3&lt;/code&gt;, you&amp;rsquo;re still not safe. You will run into the same permissions and flexibility problems described above.&lt;/p&gt;
&lt;p&gt;In addition, you don&amp;rsquo;t really have much control over what version of Python comes installed on your OS. If you want to use the latest features in Python, and you&amp;rsquo;re on Ubuntu for example, you might just be out of luck. The default versions might be too old, which means you&amp;rsquo;ll just have to wait for a new OS to come out.&lt;/p&gt;
&lt;p&gt;Finally, some operating systems actually use the packaged Python for operation. Take &lt;code&gt;yum&lt;/code&gt; for example, which makes heavy use of Python to do its job. If you install a new version of Python and aren&amp;rsquo;t careful to install it into your user space, you could seriously damage your ability to use your OS.&lt;/p&gt;
&lt;h3 id=&quot;what-about-a-package-manager&quot;&gt;What About a Package Manager?&lt;/h3&gt;
&lt;p&gt;The next logical place to look is package managers. Programs such as &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;yum&lt;/code&gt;, &lt;code&gt;brew&lt;/code&gt;, or &lt;code&gt;port&lt;/code&gt; are typical next options. After all, this is how you install most packages to your system. Unfortunately, you&amp;rsquo;ll find some of the same problems using a package manager.&lt;/p&gt;
&lt;p&gt;By default, package managers tend to install their packages into the global system space instead of the user space. Again, these system level packages pollute your development environment and make it hard to share a workspace with others.&lt;/p&gt;
&lt;p&gt;Once again, you still don&amp;rsquo;t have control over what version of Python you can install. It&amp;rsquo;s true some repositories give you a greater selection, but by default, you&amp;rsquo;re looking at whatever version of Python your particular vendor is up to on any given day.&lt;/p&gt;
&lt;p&gt;Even if you do install Python from a package manager, consider what would happen if you&amp;rsquo;re writing a package and want to support and test on Python 3.4 - 3.7.&lt;/p&gt;
&lt;p&gt;What would happen on your system when you type &lt;code&gt;python3&lt;/code&gt;? How would you switch quickly between the different versions? You can certainly do it, but it is tedious and prone to error. Nevermind the fact that if you want PyPy, Jython, or Miniconda, then you&amp;rsquo;re probably just out of luck with your package manager.&lt;/p&gt;
&lt;p&gt;With these constraints in mind, let&amp;rsquo;s recap the criteria that would let you install and manage Python versions easily and flexibly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Python in your user space&lt;/li&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Specify the exact Python version you want&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; lets you do all of these things and more.&lt;/p&gt;
&lt;h2 id=&quot;installing-pyenv&quot;&gt;Installing &lt;code&gt;pyenv&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Before you install &lt;code&gt;pyenv&lt;/code&gt; itself, you&amp;rsquo;re going to need some OS-specific dependencies. These dependencies are mostly development utilities written in C and are required because &lt;code&gt;pyenv&lt;/code&gt; installs Python by building from source. For a more detailed breakdown and explanation of the build dependencies, you can check out the &lt;a href=&quot;https://devguide.python.org/setup/#build-dependencies&quot;&gt;official docs.&lt;/a&gt; In this tutorial, you&amp;rsquo;ll see the most common ways to install these dependencies.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;pyenv&lt;/code&gt; did not originally support Windows. However, there appears to be some basic support with the &lt;a href=&quot;https://github.com/pyenv-win/pyenv-win&quot;&gt;pyenv-win&lt;/a&gt; project that recently became active. If you use Windows, feel free to check it out.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;build-dependencies&quot;&gt;Build Dependencies&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; builds Python from source, which means you&amp;rsquo;ll need build dependencies to actually use &lt;code&gt;pyenv&lt;/code&gt;. The build dependencies vary by platform. If you are on &lt;strong&gt;Ubuntu/Debian&lt;/strong&gt; and want to install the build dependencies, you could use the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo apt-get install -y make build-essential libssl-dev zlib1g-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This uses &lt;a href=&quot;https://wiki.debian.org/Apt&quot;&gt;Apt&lt;/a&gt; to install all the build dependencies. Let this run, and you&amp;rsquo;ll be ready to go for Debian systems.&lt;/p&gt;
&lt;p&gt;If you use &lt;strong&gt;Fedora/CentOS/RHEL&lt;/strong&gt;, you could use &lt;code&gt;yum&lt;/code&gt; to install your build dependencies:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; sudo yum install zlib-devel bzip2 bzip2-devel readline-devel sqlite &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
sqlite-devel openssl-devel xz xz-devel libffi-devel
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command will install all the build dependencies for Python using &lt;code&gt;yum&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt; users can use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; brew install readline xz
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command relies on &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; and installs the few dependencies for macOS users. &lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re instead using &lt;strong&gt;openSUSE&lt;/strong&gt; then you would run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; zypper in zlib-devel bzip2 libbz2-devel libffi-devel &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
libopenssl-devel readline-devel sqlite3 sqlite3-devel xz xz-devel
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once again, this command installs all the Python build dependencies for your system. &lt;/p&gt;
&lt;p&gt;Finally, for &lt;strong&gt;Alpine&lt;/strong&gt; users, you can use this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; apk add libffi-dev ncurses-dev openssl-dev readline-dev &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
tk-dev xz-dev zlib-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command uses &lt;a href=&quot;https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management&quot;&gt;&lt;code&gt;apk&lt;/code&gt;&lt;/a&gt; as the package manager and will install all build dependencies for Python on Alpine.&lt;/p&gt;
&lt;h3 id=&quot;using-the-pyenv-installer&quot;&gt;Using the pyenv-installer&lt;/h3&gt;
&lt;p&gt;After you&amp;rsquo;ve installed the build dependencies, you&amp;rsquo;re ready to install &lt;code&gt;pyenv&lt;/code&gt; itself. I recommend using the &lt;a href=&quot;https://github.com/pyenv/pyenv-installer&quot;&gt;pyenv-installer project&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; curl https://pyenv.run &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; bash
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will install &lt;code&gt;pyenv&lt;/code&gt; along with a few plugins that are useful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv&lt;/code&gt;&lt;/strong&gt;: The actual &lt;code&gt;pyenv&lt;/code&gt; application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-virtualenv&lt;/code&gt;&lt;/strong&gt;: Plugin for &lt;code&gt;pyenv&lt;/code&gt; and virtual environments&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-update&lt;/code&gt;&lt;/strong&gt;: Plugin for updating &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-doctor&lt;/code&gt;&lt;/strong&gt;: Plugin to verify that &lt;code&gt;pyenv&lt;/code&gt; and build dependencies are installed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pyenv-which-ext&lt;/code&gt;&lt;/strong&gt;: Plugin to automatically lookup system commands&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The above command is the same as downloading the &lt;a href=&quot;https://github.com/pyenv/pyenv-installer/blob/master/bin/pyenv-installer&quot;&gt;pyenv-installer script&lt;/a&gt; and running it locally. So if you&amp;rsquo;d like to see exactly what you&amp;rsquo;re running, you can view the file yourself. Alternatively, if you really don&amp;rsquo;t want to run a script, you can checkout the &lt;a href=&quot;https://github.com/pyenv/pyenv#basic-github-checkout&quot;&gt;manual installation instructions.&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;At the end of the run, you should see something like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;WARNING: seems you still have not added &amp;#39;pyenv&amp;#39; to the load path.&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; Load pyenv automatically by adding
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; the following to ~/.bashrc:

&lt;span class=&quot;go&quot;&gt;export PATH=&amp;quot;$HOME/.pyenv/bin:$PATH&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;eval &amp;quot;$(pyenv init -)&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;eval &amp;quot;$(pyenv virtualenv-init -)&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output will be based on your shell. But you should follow the instructions to add &lt;code&gt;pyenv&lt;/code&gt; to your path and to initialize &lt;code&gt;pyenv&lt;/code&gt;/&lt;code&gt;pyenv-virtualenv&lt;/code&gt; auto completion. Once you&amp;rsquo;ve done this, you need to reload your shell:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SHELL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Or just restart your terminal&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That&amp;rsquo;s it. You now have &lt;code&gt;pyenv&lt;/code&gt; and four useful plugins installed.&lt;/p&gt;
&lt;h2 id=&quot;using-pyenv-to-install-python&quot;&gt;Using &lt;code&gt;pyenv&lt;/code&gt; to Install Python&lt;/h2&gt;
&lt;p&gt;Now that you have &lt;code&gt;pyenv&lt;/code&gt; installed, installing Python is the next step. You have many versions of Python to choose from. If you wanted to see all the available CPython 3.6 through 3.8, you can do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;s2&quot;&gt;&amp;quot; 3\.[678]&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.4&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.7&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.7.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above shows all the Python versions that &lt;code&gt;pyenv&lt;/code&gt; knows about that match the regular expression. In this case, that is all available CPython versions 3.6 through 3.8. Likewise, if you wanted to see all the Jython versions, you could do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; grep &lt;span class=&quot;s2&quot;&gt;&amp;quot;jython&amp;quot;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5-dev&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.5.4-rc1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.7.0&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  jython-2.7.1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, you can see all the Jython versions that &lt;code&gt;pyenv&lt;/code&gt; has to offer. If you want to see all the versions, you can do the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install --list
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;#&lt;/span&gt; There are a lot
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you find the version you want, you can install it with a single command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install -v &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.7.2
&lt;span class=&quot;go&quot;&gt;/tmp/python-build.20190208022403.30568 ~&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Downloading Python-3.7.2.tar.xz...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;-&amp;gt; https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing Python-3.7.2...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;/tmp/python-build.20190208022403.30568/Python-3.7.2 /tmp/python-build.20190208022403.30568 ~&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[...]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installing collected packages: setuptools, pip&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Successfully installed pip-18.1 setuptools-40.6.2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Installed Python-3.7.2 to /home/realpython/.pyenv/versions/3.7.2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Having Problems?&lt;/strong&gt; The &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;&lt;code&gt;pyenv&lt;/code&gt; documentation&lt;/a&gt; has great installation notes as well as a useful &lt;a href=&quot;https://github.com/pyenv/pyenv/wiki&quot;&gt;FAQ&lt;/a&gt; along with &lt;a href=&quot;https://github.com/pyenv/pyenv/wiki/Common-build-problems&quot;&gt;common build problems&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This will take a while because &lt;code&gt;pyenv&lt;/code&gt; is building Python from source, but once it&amp;rsquo;s done, you&amp;rsquo;ll have Python 3.7.2 available on your local machine. If you don&amp;rsquo;t want to see all the output, just remove the &lt;code&gt;-v&lt;/code&gt; flag. Even development versions of CPython can be installed:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you&amp;rsquo;ve been using &lt;code&gt;pyenv&lt;/code&gt; for a while and don&amp;rsquo;t see the version you&amp;rsquo;re looking for, you may need to run &lt;code&gt;pyenv update&lt;/code&gt; to update the tool and make sure you have access to the latest versions.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;For the rest of the tutorial, the examples assume you&amp;rsquo;ve installed &lt;code&gt;3.6.8&lt;/code&gt; and &lt;code&gt;2.7.15&lt;/code&gt;, but you&amp;rsquo;re free to substitute these values for the Python versions you actually installed. Also note that the system Python version in the examples is &lt;code&gt;2.7.12&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;installation-location&quot;&gt;Installation Location&lt;/h3&gt;
&lt;p&gt;As mentioned before, &lt;code&gt;pyenv&lt;/code&gt; works by building Python from source. Each version that you have installed is located nicely in your &lt;code&gt;pyenv&lt;/code&gt; root directory:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls ~/.pyenv/versions/
&lt;span class=&quot;go&quot;&gt;2.7.15  3.6.8  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All of your versions will be located here. This is handy because removing these versions is trivial:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; rm -rf ~/.pyenv/versions/2.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Of course &lt;code&gt;pyenv&lt;/code&gt; also provides a command to uninstall a particular Python version:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv uninstall &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-your-new-python&quot;&gt;Using Your New Python&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve installed a couple of different Python versions, let&amp;rsquo;s see some basics on how to use them. First, check what versions of Python you have available:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; indicates that the &lt;code&gt;system&lt;/code&gt; Python version is active currently. You&amp;rsquo;ll also notice that this is set by a file in your root &lt;code&gt;pyenv&lt;/code&gt; directory. This means that, by default, you are still using your system Python:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you try to confirm this using &lt;code&gt;which&lt;/code&gt;, you&amp;rsquo;ll see this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/shims/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This might be surprising, but this is how &lt;code&gt;pyenv&lt;/code&gt; works. &lt;code&gt;pyenv&lt;/code&gt; inserts itself into your &lt;code&gt;PATH&lt;/code&gt; and from your OS&amp;rsquo;s perspective &lt;em&gt;is&lt;/em&gt; the executable that is getting called. If you want to see the actual path, you can run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If, for example, you wanted to use version 2.7.15, then you can use the &lt;code&gt;global&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.15&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; A great way to get peace of mind that the version of Python you just installed is working properly is to run the built-in test suite:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -m &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will kick off lots of internal Python tests that will verify your installation. You can just kick back and watch the tests pass.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;If you ever want to go back to the system version of Python as the default, you can run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global system
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 2.7.12&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can now switch between different versions of Python with ease. This is just the beginning. If you have many versions that you want to switch between, typing these commands consistently is tedious. This section goes over the basics, but a better workflow is described in &lt;a href=&quot;#working-with-multiple-environments&quot;&gt;working with multiple environments.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;exploring-pyenv-commands&quot;&gt;Exploring &lt;code&gt;pyenv&lt;/code&gt; Commands&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; offers many commands. You can see a complete list of all available commands with this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv commands
&lt;span class=&quot;go&quot;&gt;activate&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;commands&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;completions&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;deactivate&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;virtualenvs&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;whence&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;which&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This outputs all command names. Each command has a &lt;code&gt;--help&lt;/code&gt; flag that will give you more detailed information. For example, if you wanted to see more information on the &lt;code&gt;shims&lt;/code&gt; command you could run the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shims --help
&lt;span class=&quot;go&quot;&gt;Usage: pyenv shims [--short]&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;List existing pyenv shims&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The help message describes what the command is used for and any options you can use in conjunction with the command. In the following sections, you&amp;rsquo;ll find a quick, high-level overview of the most used commands.&lt;/p&gt;
&lt;h3 id=&quot;install&quot;&gt;&lt;code&gt;install&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ve already seen the &lt;code&gt;install&lt;/code&gt; command above. This command can be used to install a specific version of Python. For example, if you wanted to install &lt;code&gt;3.6.8&lt;/code&gt; you would use this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv install &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output shows us &lt;code&gt;pyenv&lt;/code&gt; downloading and installing Python. Some of the common flags you may want to use are the following:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l/--list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists all available Python versions for installation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-g/--debug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds a debug version of Python&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v/--verbose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verbose mode: print compilation status to stdout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;versions&quot;&gt;&lt;code&gt;versions&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;versions&lt;/code&gt; command displays all currently installed Python versions:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This output shows not only that &lt;code&gt;2.7.15&lt;/code&gt;, &lt;code&gt;3.6.8&lt;/code&gt;, &lt;code&gt;3.8-dev&lt;/code&gt;, and your &lt;code&gt;system&lt;/code&gt; Python are installed, but also shows you that the &lt;code&gt;system&lt;/code&gt; Python is active. If you only care about the current active version, you can use the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv version
&lt;span class=&quot;go&quot;&gt;system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command is similar to &lt;code&gt;versions&lt;/code&gt; but only shows you the current active Python version.&lt;/p&gt;
&lt;h3 id=&quot;which&quot;&gt;&lt;code&gt;which&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;which&lt;/code&gt; command is helpful for determining the full path to a system executable. Because &lt;code&gt;pyenv&lt;/code&gt; works by using shims, this command allows you to see the full path to the executable &lt;code&gt;pyenv&lt;/code&gt; is running. For example, if you wanted to see where &lt;code&gt;pip&lt;/code&gt; is installed, you could run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which pip
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/3.6.8/bin/pip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output displays the full system path for &lt;code&gt;pip&lt;/code&gt;. This can be helpful when you&amp;rsquo;ve installed command-line applications.&lt;/p&gt;
&lt;h3 id=&quot;global&quot;&gt;&lt;code&gt;global&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;global&lt;/code&gt; command sets the global Python version. This can be overridden with other commands, but is useful for ensuring you use a particular Python version by default. If you wanted to use &lt;code&gt;3.6.8&lt;/code&gt; by default, then you could run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command sets the &lt;code&gt;~/.pyenv/version&lt;/code&gt; to &lt;code&gt;3.6.8&lt;/code&gt;. For more information, see the section on &lt;a href=&quot;#specifying-your-python-version&quot;&gt;specifying your Python version&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;local&quot;&gt;&lt;code&gt;local&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;local&lt;/code&gt; command is often used to set an application-specific Python version. You could use it to set the version to &lt;code&gt;2.7.15&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command creates a &lt;code&gt;.python-version&lt;/code&gt; file in your current directory. If you have &lt;code&gt;pyenv&lt;/code&gt; active in your environment, this file will automatically activate this version for you.&lt;/p&gt;
&lt;h3 id=&quot;shell&quot;&gt;&lt;code&gt;shell&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;shell&lt;/code&gt; command is used to set a shell-specific Python version. For example, if you wanted to test out the &lt;code&gt;3.8-dev&lt;/code&gt; version of Python, you can do this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shell &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command activates the version specified by setting the &lt;code&gt;PYENV_VERSION&lt;/code&gt; environment variable. This command overwrites any applications or global settings you may have. If you want to deactivate the version, you can use the &lt;code&gt;--unset&lt;/code&gt; flag.&lt;/p&gt;
&lt;h2 id=&quot;specifying-your-python-version&quot;&gt;Specifying Your Python Version&lt;/h2&gt;
&lt;p&gt;One of the more confusing parts of &lt;code&gt;pyenv&lt;/code&gt; is how exactly the &lt;code&gt;python&lt;/code&gt; command gets resolved and what commands can be used to modify it. As mentioned in the commands, there are 3 ways to modify which version of &lt;code&gt;python&lt;/code&gt; you&amp;rsquo;re using. So how do all these commands interact with one another? The resolution order looks a little something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&quot; width=&quot;751&quot; height=&quot;786&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&amp;amp;w=187&amp;amp;sig=1b68b85f9a3123f477e960b1623452e1b2b92659 187w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png&amp;amp;w=375&amp;amp;sig=1d79c37a5123a7ea60c63989e2785e06aa29faa3 375w, https://files.realpython.com/media/pyenv-pyramid.d2f35a19ded9.png 751w&quot; sizes=&quot;75vw&quot; alt=&quot;Pyenv pyramid for order of resolution&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This pyramid is meant to be read from top to bottom. The first of these options that &lt;code&gt;pyenv&lt;/code&gt; can find is the option it will use. Let&amp;rsquo;s see a quick example:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, your &lt;code&gt;system&lt;/code&gt; Python is being used as denoted by the &lt;code&gt;*&lt;/code&gt;. To exercise the next most global setting, you use &lt;code&gt;global&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv global &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 3.6.8 (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see that now &lt;code&gt;pyenv&lt;/code&gt; wants to use &lt;code&gt;3.6.8&lt;/code&gt; as our Python version. It even indicates the location of the file it found. That file does indeed exist, and you can list its contents:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat ~/.pyenv/version
&lt;span class=&quot;go&quot;&gt;3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let&amp;rsquo;s create a &lt;code&gt;.python-version&lt;/code&gt; file with &lt;code&gt;local&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;.7.15
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.python-version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls -a
&lt;span class=&quot;go&quot;&gt;.  ..  .python-version&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; cat .python-version
&lt;span class=&quot;go&quot;&gt;2.7.15&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here again, &lt;code&gt;pyenv&lt;/code&gt; indicates how it would resolve our &lt;code&gt;python&lt;/code&gt; command. This time it comes from &lt;code&gt;~/.python-version&lt;/code&gt;. Note that the searching for &lt;code&gt;.python-version&lt;/code&gt; is recursive:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; mkdir subdirectory
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; subdirectory
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls -la &lt;span class=&quot;c1&quot;&gt;# Notice no .python-version file&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;. ..&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 2.7.15 (set by /home/realpython/.python-version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though there isn&amp;rsquo;t a &lt;code&gt;.python-version&lt;/code&gt; in &lt;code&gt;subdirectory&lt;/code&gt;, the version is still set to &lt;code&gt;2.7.15&lt;/code&gt; because &lt;code&gt;.python-version&lt;/code&gt; exists in a parent directory.&lt;/p&gt;
&lt;p&gt;Finally, you can set the Python version with &lt;code&gt;shell&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv shell &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;  system&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;* 3.8-dev (set by PYENV_VERSION environment variable)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All this did is set the &lt;code&gt;$PYENV_VERSION&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PYENV_VERSION&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you&amp;rsquo;re feeling overwhelmed by the options, the section on &lt;a href=&quot;#working-with-multiple-environments&quot;&gt;working with multiple environments&lt;/a&gt; goes over an opinionated process for managing these files, mostly using &lt;code&gt;local&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;virtual-environments-and-pyenv&quot;&gt;Virtual Environments and &lt;code&gt;pyenv&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Virtual environments are a big part of managing Python installations and applications. If you haven&amp;rsquo;t heard of virtual environments before, you can check out &lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;&gt;Python Virtual Environments: A Primer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Virtual environments and &lt;code&gt;pyenv&lt;/code&gt; are a match made in heaven. &lt;code&gt;pyenv&lt;/code&gt; has a wonderful plugin called &lt;a href=&quot;https://github.com/pyenv/pyenv-virtualenv&quot;&gt;&lt;code&gt;pyenv-virtualenv&lt;/code&gt;&lt;/a&gt; that makes working with multiple Python version &lt;em&gt;and&lt;/em&gt; multiple virtual environments a breeze. If you&amp;rsquo;re wondering what the difference is between &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;pyenv-virtualenv&lt;/code&gt;, and tools like &lt;code&gt;virtualenv&lt;/code&gt; or &lt;code&gt;venv&lt;/code&gt;, then don&amp;rsquo;t worry. You&amp;rsquo;re not alone.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what you need to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;pyenv&lt;/strong&gt; manages multiple versions of Python itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;virtualenv/venv&lt;/strong&gt; manages virtual environments for a specific Python version.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pyenv-virtualenv&lt;/strong&gt; manages virtual environments for across varying versions of Python.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;re a die-hard &lt;code&gt;virtualenv&lt;/code&gt; or &lt;code&gt;venv&lt;/code&gt; user, don&amp;rsquo;t worry: &lt;code&gt;pyenv&lt;/code&gt; plays nicely with either. In fact, you can keep the same workflow you&amp;rsquo;ve had if you&amp;rsquo;d prefer, though I think &lt;code&gt;pyenv-virtualenv&lt;/code&gt; makes for a nicer experience when you&amp;rsquo;re switching between multiple environments that require different Python versions.&lt;/p&gt;
&lt;p&gt;The good news is that since you used the &lt;code&gt;pyenv-installer&lt;/code&gt; script to install &lt;code&gt;pyenv&lt;/code&gt;, you already have &lt;code&gt;pyenv-virtualenv&lt;/code&gt; installed and ready to go.&lt;/p&gt;
&lt;h3 id=&quot;creating-virtual-environments&quot;&gt;Creating Virtual Environments&lt;/h3&gt;
&lt;p&gt;Creating a virtual environment is a single command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &amp;lt;python_version&amp;gt; &amp;lt;environment_name&amp;gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Technically, the &lt;code&gt;&amp;lt;python_version&amp;gt;&lt;/code&gt; is optional, but you should consider always specifying it so that you&amp;rsquo;re certain of what Python version you&amp;rsquo;re using.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;environment_name&amp;gt;&lt;/code&gt; is just a name for you to help keep your environments separate. A good practice is to name your environments the same name as your project. For example, if you were working on &lt;code&gt;myproject&lt;/code&gt; and wanted to develop against Python 3.6.8, you would run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 myproject
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output includes messages that show a couple of extra Python packages getting installed, namely &lt;code&gt;wheel&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;, and &lt;code&gt;setuptools&lt;/code&gt;. This is strictly for convenience and just sets up a more full featured environment for each of your virtual environments.&lt;/p&gt;
&lt;h3 id=&quot;activating-your-versions&quot;&gt;Activating Your Versions&lt;/h3&gt;
&lt;p&gt;Now that you&amp;rsquo;ve created your virtual environment, using it is the next step. Normally, you should activate your environments by running the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; myproject
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You&amp;rsquo;ve seen the &lt;code&gt;pyenv local&lt;/code&gt; command before, but this time, instead of specifying a Python version, you specify an environment. This creates a &lt;code&gt;.python-version&lt;/code&gt; file in your current working directory and because you ran &lt;code&gt;eval &quot;$(pyenv virtualenv-init -)&quot;&lt;/code&gt; in your environment, the environment will automatically be activated.&lt;/p&gt;
&lt;p&gt;You can verify this by running the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/myproject/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see a new version has been created called &lt;code&gt;myproject&lt;/code&gt; and the &lt;code&gt;python&lt;/code&gt; executable is pointing to that version. If you look at any executable this environment provides, you will see the same thing. Take, for example, &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which pip
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/myproject/bin/pip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you did not configure &lt;code&gt;eval &quot;$(pyenv virtualenv-init -)&quot;&lt;/code&gt; to run in your shell, you can manually activate/deactivate your Python versions with this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv activate &amp;lt;environment_name&amp;gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv deactivate
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The above is what &lt;code&gt;pyenv-virtualenv&lt;/code&gt; is doing when it enters or exits a directory with a &lt;code&gt;.python-version&lt;/code&gt; file in it.&lt;/p&gt;
&lt;h2 id=&quot;working-with-multiple-environments&quot;&gt;Working With Multiple Environments&lt;/h2&gt;
&lt;p&gt;Putting everything you&amp;rsquo;ve learned together, you can work effectively with multiple environments. Let&amp;rsquo;s assume you have the following versions installed:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv versions
&lt;span class=&quot;go&quot;&gt;* system (set by /home/realpython/.pyenv/version)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  2.7.15&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.8-dev&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you want to work on two different, aptly named, projects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;project1&lt;/strong&gt; supports Python 2.7  and 3.6.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;project2&lt;/strong&gt; supports Python 3.6 and experiments with 3.8-dev.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see that, by default, you are using the system Python, which is indicated by the &lt;code&gt;*&lt;/code&gt; in the &lt;code&gt;pyenv versions&lt;/code&gt; output. First, create a virtual environment for the first project:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project1/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 project1
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project1
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/project1/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, notice that when you &lt;code&gt;cd&lt;/code&gt; out of the directory, you default back to the system Python:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can follow the above steps and create a virtual environment for project2:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/usr/bin/python&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev project2
&lt;span class=&quot;go&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.8-dev
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv which python
&lt;span class=&quot;go&quot;&gt;/home/realpython/.pyenv/versions/3.8-dev/bin/python&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are one time steps for your projects. Now, as you &lt;code&gt;cd&lt;/code&gt; between the projects, your environments will automatically activate:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 3.8.0a0&lt;/span&gt;
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../project1
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python -V
&lt;span class=&quot;go&quot;&gt;Python 3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;No more remembering to activate environments: you can switch between all your projects, and &lt;code&gt;pyenv&lt;/code&gt; will take care of automatically activating the correct Python versions &lt;em&gt;and&lt;/em&gt; the correct virtual environments.&lt;/p&gt;
&lt;h2 id=&quot;activating-multiple-versions-simultaneously&quot;&gt;Activating Multiple Versions Simultaneously&lt;/h2&gt;
&lt;p&gt;As described in the example above, &lt;code&gt;project2&lt;/code&gt; uses experimental features in 3.8. Suppose you wanted to ensure that your code still works on Python 3.6. If you try running &lt;code&gt;python3.6&lt;/code&gt;, you&amp;rsquo;ll get this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; project2/
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.6 -V
&lt;span class=&quot;go&quot;&gt;pyenv: python3.6: command not found&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;The `python3.6&amp;#39; command exists in these Python versions:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  3.6.8/envs/project1&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  project1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pyenv&lt;/code&gt; informs you that, while Python 3.6 is not available in the current active environment, it is available in other environments. &lt;code&gt;pyenv&lt;/code&gt; gives you a way to activate multiple environments at once using a familiar command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2 &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This indicates to &lt;code&gt;pyenv&lt;/code&gt; that you would like to use the virtual environment &lt;code&gt;project2&lt;/code&gt; as the first option. So if a command, for example &lt;code&gt;python&lt;/code&gt;, can be resolved in both environments, it will pick &lt;code&gt;project2&lt;/code&gt; before &lt;code&gt;3.6.8&lt;/code&gt;. Let&amp;rsquo;s see what happens if you run this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; python3.6 -V
&lt;span class=&quot;go&quot;&gt;Python 3.6.8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;pyenv&lt;/code&gt; attempts to find the &lt;code&gt;python3.6&lt;/code&gt; command, and because it finds it in an environment that is active, it allows the command to execute. This is extremely useful for tools like &lt;a href=&quot;https://tox.readthedocs.io/en/latest/&quot;&gt;tox&lt;/a&gt; that require multiple versions of Python to be available on your &lt;code&gt;PATH&lt;/code&gt; in order to execute.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; If you&amp;rsquo;re using tox and &lt;code&gt;pyenv&lt;/code&gt;, you should checkout the &lt;a href=&quot;https://pypi.org/project/tox-pyenv/&quot;&gt;tox-pyenv&lt;/a&gt; package.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Suppose that in the above example, you&amp;rsquo;ve found a compatibility problem with your library and would like to do some local testing. The testing requires that you install all the dependencies. You should follow the steps to create a new environment:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv virtualenv &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8 project2-tmp
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2-tmp
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once you&amp;rsquo;re satisfied with your local testing, you can easily switch back to your default environment:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyenv &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; project2 &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.6.8
&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You can now more easily contribute to a project that wants to support multiple environments. You can also more easily test out the latest and greatest Python versions without having to worry about messing up your development machine, all with a wonderful tool: &lt;code&gt;pyenv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve seen how &lt;code&gt;pyenv&lt;/code&gt; can help you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install multiple versions of Python&lt;/li&gt;
&lt;li&gt;Switch between the installed versions&lt;/li&gt;
&lt;li&gt;Use virtual environments with &lt;code&gt;pyenv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Activate different Python versions and virtual environments automatically&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you still have questions, feel free to reach out either in the comments section or on Twitter. Additionally, the &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv documentation&lt;/a&gt; is a great resource.&lt;/p&gt;
&lt;h2 id=&quot;bonus-displaying-your-environment-name-in-your-command-prompt&quot;&gt;Bonus: Displaying Your Environment Name in Your Command Prompt&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re like me and constantly switching between various virtual environments and Python versions, it&amp;rsquo;s easy to get confused about which version is currently active. I use &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot;&gt;oh-my-zsh&lt;/a&gt; and the &lt;a href=&quot;https://github.com/agnoster/agnoster-zsh-theme&quot;&gt;agnoster theme&lt;/a&gt;, which by default makes my prompt look like this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&quot; width=&quot;1087&quot; height=&quot;393&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&amp;amp;w=271&amp;amp;sig=af4cd711e388ddcb715370bfabe325212a90cc90 271w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png&amp;amp;w=543&amp;amp;sig=1bbe7d3f47f4a1c8bf8be5936c916262b816cfa0 543w, https://files.realpython.com/media/agnoster-no-pyenv-prompt.01f0966b78d7.png 1087w&quot; sizes=&quot;75vw&quot; alt=&quot;Agnoster theme with no pyenv prompt&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At a glance, I don&amp;rsquo;t know which Python version is active. To figure it out, I would have to run &lt;code&gt;python -V&lt;/code&gt; or &lt;code&gt;pyenv version&lt;/code&gt;. To help reduce my time spent on figuring out my active Python environment, I add the &lt;code&gt;pyenv&lt;/code&gt; virtual environment I&amp;rsquo;m using to my prompt:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&quot; width=&quot;1089&quot; height=&quot;378&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&amp;amp;w=272&amp;amp;sig=a1b3eeb3dddcf15e0e0bad3519d63508e5c8f6ed 272w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png&amp;amp;w=544&amp;amp;sig=50c98a638b1aeeb42a54d37d828beb718981c7ac 544w, https://files.realpython.com/media/ljones-agnoster-pyenv-prompt.0b24a4a8f077.png 1089w&quot; sizes=&quot;75vw&quot; alt=&quot;Environment name being shown in ZSH command prompt&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My Python version in this case is &lt;code&gt;project1-venv&lt;/code&gt; and is displayed immediately at the beginning of the prompt. This allows me to quickly see what version of Python I&amp;rsquo;m using right away. If you&amp;rsquo;d like to use this too, you can use my &lt;a href=&quot;https://gist.github.com/loganasherjones/bd9b7614f80b96cf700fd60e9e256f41&quot;&gt;agnoster-pyenv theme&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Writing Comments in Python</title>
      <id>https://realpython.com/courses/writing-comments-python/</id>
      <link href="https://realpython.com/courses/writing-comments-python/"/>
      <updated>2019-03-08T00:02:05+00:00</updated>
      <summary>Learn how to write Python comments that are clean, concise, and useful. Quickly get up to speed on what the best practices are, which types of comments it&#39;s best to avoid, and how you can practice writing cleaner comments.</summary>
      <content type="html">
        &lt;p&gt;Learn how to write Python comments that are clean, concise, and useful. Quickly get up to speed on what the best practices are, which types of comments it&amp;rsquo;s best to avoid, and how you can practice writing cleaner comments.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Making HTTP Requests With Python</title>
      <id>https://realpython.com/courses/python-requests/</id>
      <link href="https://realpython.com/courses/python-requests/"/>
      <updated>2019-03-06T15:17:19+00:00</updated>
      <summary>The &quot;requests&quot; library is the de facto standard for making HTTP requests in Python. It abstracts the complexities of making requests behind a beautiful, simple API so that you can focus on interacting with services and consuming data in your application. This course shows you how to work effectively with &quot;requests&quot;, from start to finish.</summary>
      <content type="html">
        &lt;p&gt;The &lt;code&gt;requests&lt;/code&gt; library is the de facto standard for making HTTP requests in Python. It abstracts the complexities of making requests behind a beautiful, simple API so that you can focus on interacting with services and consuming data in your application.&lt;/p&gt;
&lt;p&gt;Throughout this course, you’ll see some of the most useful features that requests has to offer as well as how to customize and optimize those features for different situations you may come across. You’ll also learn how to use requests in an efficient way as well as how to prevent requests to external services from slowing down your application.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Using PyInstaller to Easily Distribute Python Applications</title>
      <id>https://realpython.com/pyinstaller-python/</id>
      <link href="https://realpython.com/pyinstaller-python/"/>
      <updated>2019-03-06T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to use PyInstaller to turn your Python application into an executable with no dependencies or installation required. This is great if you want to distribute applications to users who may or may not be Python developers.</summary>
      <content type="html">
        &lt;p&gt;Are you jealous of &lt;a href=&quot;https://golang.org&quot;&gt;Go&lt;/a&gt; developers building an executable and easily shipping it to users? Wouldn&amp;rsquo;t it be great if your users could &lt;strong&gt;run your application without installing anything&lt;/strong&gt;? That is the dream, and &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/&quot;&gt;PyInstaller&lt;/a&gt; is one way to get there in the Python ecosystem.&lt;/p&gt;
&lt;p&gt;There are countless tutorials on how to &lt;a href=&quot;https://realpython.com/dependency-pitfalls/&quot;&gt;set up virtual environments&lt;/a&gt;, &lt;a href=&quot;https://realpython.com/products/managing-python-dependencies/&quot;&gt;manage dependencies&lt;/a&gt;, and &lt;a href=&quot;https://realpython.com/pypi-publish-python-package/&quot;&gt;publish to PyPI&lt;/a&gt;, which is useful when you&amp;rsquo;re creating Python libraries. There is much less information for &lt;strong&gt;developers building Python applications&lt;/strong&gt;. This tutorial is for developers who want to distribute applications to users who may or may not be Python developers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial, you&amp;rsquo;ll learn the following:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How PyInstaller can simplify application distribution&lt;/li&gt;
&lt;li&gt;How to use PyInstaller on your own projects&lt;/li&gt;
&lt;li&gt;How to debug PyInstaller errors&lt;/li&gt;
&lt;li&gt;What PyInstaller can&amp;rsquo;t do&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/&quot;&gt;PyInstaller&lt;/a&gt; gives you the ability to create a folder or executable that users can immediately run without any extra installation. To fully appreciate PyInstaller&amp;rsquo;s power, it&amp;rsquo;s useful to revisit some of the distribution problems PyInstaller helps you avoid.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;distribution-problems&quot;&gt;Distribution Problems&lt;/h2&gt;
&lt;p&gt;Setting up a Python project can be frustrating, especially for non-developers. Often, the setup starts with opening a Terminal, which is a non-starter for a huge group of potential users. This roadblock stops users even before the installation guide delves into the complicated details of virtual environments, Python versions, and the myriad of potential dependencies.&lt;/p&gt;
&lt;p&gt;Think about what you typically go through when setting up a new machine for Python development. It probably goes something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download and install a specific version of Python&lt;/li&gt;
&lt;li&gt;Set up pip&lt;/li&gt;
&lt;li&gt;Set up a virtual environment&lt;/li&gt;
&lt;li&gt;Get a copy of your code&lt;/li&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stop for a moment and consider if any of the above steps make any sense if you&amp;rsquo;re not a developer, let alone a Python developer. Probably not.&lt;/p&gt;
&lt;p&gt;These problems explode if your user is lucky enough to get to the dependencies portion of the installation. This has gotten much better in the last few years with the prevalence of wheels, but some dependencies still require C/C++ or even FORTRAN compilers!&lt;/p&gt;
&lt;p&gt;This barrier to entry is way too high if your goal is to make an application available to as many users as possible. As &lt;a href=&quot;https://mobile.twitter.com/raymondh&quot;&gt;Raymond Hettinger&lt;/a&gt; often says in his &lt;a href=&quot;https://pyvideo.org/speaker/raymond-hettinger.html&quot;&gt;excellent talks&lt;/a&gt;, &amp;ldquo;There has to be a better way.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&quot;pyinstaller&quot;&gt;PyInstaller&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/&quot;&gt;PyInstaller&lt;/a&gt; abstracts these details from the user by finding all your dependencies and bundling them together. Your users won&amp;rsquo;t even know they&amp;rsquo;re running a Python project because the Python Interpreter itself is bundled into your application. Goodbye complicated installation instructions!&lt;/p&gt;
&lt;p&gt;PyInstaller performs this amazing feat by &lt;a href=&quot;https://en.wikipedia.org/wiki/Type_introspection&quot;&gt;introspecting&lt;/a&gt; your Python code, detecting your dependencies, and then packaging them into a suitable format depending on your Operating System.&lt;/p&gt;
&lt;p&gt;There are lots of interesting details about PyInstaller, but for now you&amp;rsquo;ll learn the basics of how it works and how to use it. You can always refer to the &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/operating-mode.html#analysis-finding-the-files-your-program-needs&quot;&gt;excellent PyInstaller docs&lt;/a&gt; if you want more details.&lt;/p&gt;
&lt;p&gt;In addition, PyInstaller can create executables for Windows, Linux, or macOS. This means Windows users will get a &lt;code&gt;.exe&lt;/code&gt;, Linux users get a regular executable, and macOS users get a &lt;code&gt;.app&lt;/code&gt; bundle. There are some caveats to this. See the &lt;a href=&quot;#limitations&quot;&gt;limitations&lt;/a&gt; section for more information.&lt;/p&gt;
&lt;h2 id=&quot;preparing-your-project&quot;&gt;Preparing Your Project&lt;/h2&gt;
&lt;p&gt;PyInstaller requires your application to conform to some minimal structure, namely that you have a CLI script to start your application. Often, this means creating a small script &lt;em&gt;outside&lt;/em&gt; of your Python package that simply imports your package and runs &lt;code&gt;main()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The entry-point script is a Python script. You can technically do anything you want in the entry-point script, but you should avoid using &lt;a href=&quot;https://realpython.com/absolute-vs-relative-python-imports/#relative-imports&quot;&gt;explicit relative imports&lt;/a&gt;. You can still use relative imports throughout the rest your application if that&amp;rsquo;s your preferred style.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; An entry-point is the code that starts your project or application.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You can give this a try with your own project or follow along with the &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;Real Python feed reader project&lt;/a&gt;. For more detailed information on the &lt;a href=&quot;https://github.com/realpython/reader&quot;&gt;reader project&lt;/a&gt;, check out the the tutorial on &lt;a href=&quot;https://realpython.com/pypi-publish-python-package/&quot;&gt;Publishing a Package on PyPI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The first step to building an executable version of this project is to add the entry-point script. Luckily, the feed reader project is well structured, so all you need is a short script &lt;em&gt;outside the package&lt;/em&gt; to run it. For example, you can create a file called &lt;code&gt;cli.py&lt;/code&gt; alongside the reader package with the following code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;reader.__main__&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code&gt;cli.py&lt;/code&gt; script calls &lt;code&gt;main()&lt;/code&gt; to start up the feed reader.&lt;/p&gt;
&lt;p&gt;Creating this entry-point script is straightforward when you&amp;rsquo;re working on your own project because you&amp;rsquo;re familiar with the code. However, it&amp;rsquo;s not as easy to find the entry-point of another person&amp;rsquo;s code. In this case, you can start by looking at the &lt;code&gt;setup.py&lt;/code&gt; file in the third-party project.&lt;/p&gt;
&lt;p&gt;Look for a reference to the &lt;code&gt;entry_points&lt;/code&gt; argument in the project&amp;rsquo;s &lt;code&gt;setup.py&lt;/code&gt;. For example, here&amp;rsquo;s the reader project&amp;rsquo;s &lt;code&gt;setup.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;realpython-reader&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;1.0.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Read the latest Real Python tutorials&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;long_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;README&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;long_description_content_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;text/markdown&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;https://github.com/realpython/reader&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Real Python&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;author_email&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;office@realpython.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;license&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;MIT&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;classifiers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;License :: OSI Approved :: MIT License&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;Programming Language :: Python&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;Programming Language :: Python :: 2&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;Programming Language :: Python :: 3&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;reader&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;include_package_data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;install_requires&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&amp;quot;feedparser&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;html2text&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;importlib_resources&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;typing&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;entry_points&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;console_scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;realpython=reader.__main__:main&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, the entry-point &lt;code&gt;cli.py&lt;/code&gt; script calls the same function mentioned in the &lt;code&gt;entry_points&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;After this change, the reader project directory should look like this, assuming you checked it out into a folder called &lt;code&gt;reader&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;reader/
|
├── reader/
|   ├── __init__.py
|   ├── __main__.py
|   ├── config.cfg
|   ├── feed.py
|   └── viewer.py
|
├── cli.py
├── LICENSE
├── MANIFEST.in
├── README.md
├── setup.py
└── tests
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice there is no change to the reader code itself, just a new file called &lt;code&gt;cli.py&lt;/code&gt;. This entry-point script is usually all that&amp;rsquo;s necessary to use your project with PyInstaller.&lt;/p&gt;
&lt;p&gt;However, you&amp;rsquo;ll also want to look out for uses of &lt;code&gt;__import__()&lt;/code&gt; or imports inside of functions. These are referred to as &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/when-things-go-wrong.html?highlight=Hidden#listing-hidden-imports&quot;&gt;hidden imports&lt;/a&gt; in PyInstaller terminology.&lt;/p&gt;
&lt;p&gt;You can manually specify the hidden imports to force PyInstaller to include those dependencies if changing the imports in your application is too difficult. You&amp;rsquo;ll see how to do this later in this tutorial.&lt;/p&gt;
&lt;p&gt;Once you can launch your application with a Python script &lt;em&gt;outside&lt;/em&gt; of your package, you&amp;rsquo;re ready to give PyInstaller a try at creating an executable.&lt;/p&gt;
&lt;h2 id=&quot;using-pyinstaller&quot;&gt;Using PyInstaller&lt;/h2&gt;
&lt;p&gt;The first step is to install PyInstaller from &lt;a href=&quot;https://pypi.org&quot;&gt;PyPI&lt;/a&gt;. You can do this using &lt;code&gt;pip&lt;/code&gt; like other Python packages:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install pyinstaller
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; will install PyInstaller&amp;rsquo;s dependencies along with a new command:
&lt;code&gt;pyinstaller&lt;/code&gt;. PyInstaller can be imported in your Python code and used as a library, but you&amp;rsquo;ll likely only use it as a CLI tool.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll use the library interface if you &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/hooks.html#&quot;&gt;create your own hook files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll increase the likelihood of PyInstaller&amp;rsquo;s defaults creating an executable if you only have pure Python dependencies. However, don&amp;rsquo;t stress too much if you have more complicated dependencies with C/C++ extensions.&lt;/p&gt;
&lt;p&gt;PyInstaller supports lots of popular packages like &lt;a href=&quot;http://www.numpy.org&quot;&gt;NumPy&lt;/a&gt;, &lt;a href=&quot;https://pypi.org/project/PyQt5/&quot;&gt;PyQt&lt;/a&gt;, and &lt;a href=&quot;https://matplotlib.org&quot;&gt;Matplotlib&lt;/a&gt; without any additional work from you. You can see more about the list of packages that PyInstaller officially supports by referring to the &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/wiki/Supported-Packages&quot;&gt;PyInstaller documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t worry if some of your dependencies aren&amp;rsquo;t listed in the official docs. Many Python packages work fine. In fact, PyInstaller is popular enough that many projects have explanations on how to get things working with PyInstaller.&lt;/p&gt;
&lt;p&gt;In short, the chances of your project working out of the box are high. &lt;/p&gt;
&lt;p&gt;To try creating an executable with all the defaults, simply give PyInstaller the name of your main entry-point script.&lt;/p&gt;
&lt;p&gt;First, &lt;code&gt;cd&lt;/code&gt; in the folder with your entry-point and pass it as an argument to the &lt;code&gt;pyinstaller&lt;/code&gt; command that was added to your &lt;code&gt;PATH&lt;/code&gt; when PyInstaller was installed.&lt;/p&gt;
&lt;p&gt;For example, type the following after you &lt;code&gt;cd&lt;/code&gt; into the top-level &lt;code&gt;reader&lt;/code&gt; directory if you&amp;rsquo;re following along with the feed reader project:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Don&amp;rsquo;t be alarmed if you see a lot of output while building your executable. PyInstaller is verbose by default, and the verbosity can be cranked way up for debugging, which you&amp;rsquo;ll see later.&lt;/p&gt;
&lt;h2 id=&quot;digging-into-pyinstaller-artifacts&quot;&gt;Digging Into PyInstaller Artifacts&lt;/h2&gt;
&lt;p&gt;PyInstaller is complicated under the hood and will create a lot of output. So, it&amp;rsquo;s important to know what to focus on first. Namely, the executable you can distribute to your users and potential debugging information. By default, the &lt;code&gt;pyinstaller&lt;/code&gt; command will create a few things of interest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;*.spec&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;build/&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;dist/&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;spec-file&quot;&gt;Spec File&lt;/h3&gt;
&lt;p&gt;The spec file will be named after your CLI script by default. Sticking with our previous example, you&amp;rsquo;ll see a file called &lt;code&gt;cli.spec&lt;/code&gt;. Here&amp;rsquo;s what the default spec file looks like after running PyInstaller on the &lt;code&gt;cli.py&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# -*- mode: python -*-&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;block_cipher&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Analysis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cli.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;pathex&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;/Users/realpython/pyinstaller/reader&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;binaries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;datas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;hiddenimports&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;hookspath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;runtime_hooks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;excludes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;win_no_prefer_redirects&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;win_private_assemblies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block_cipher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;noarchive&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pyz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PYZ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zipped_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;cipher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;block_cipher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;exe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EXE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pyz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scripts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;exclude_binaries&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cli&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;bootloader_ignore_signals&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;upx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;coll&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;COLLECT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;binaries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zipfiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;upx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cli&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This file will be automatically created by the &lt;code&gt;pyinstaller&lt;/code&gt; command. Your version will have different paths, but the majority should be the same.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t worry, you don&amp;rsquo;t need to understand the above code to effectively use PyInstaller!&lt;/p&gt;
&lt;p&gt;This file can be modified and re-used to create executables later. You can make future builds a bit faster by providing this spec file instead of the entry-point script to the &lt;code&gt;pyinstaller&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;There are a few &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/spec-files.html#using-spec-files&quot;&gt;specific use-cases for PyInstaller spec files&lt;/a&gt;. However, for simple projects, you won&amp;rsquo;t need to worry about those details unless you want to heavily customize how your project is built.&lt;/p&gt;
&lt;h3 id=&quot;build-folder&quot;&gt;Build Folder&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;build/&lt;/code&gt; folder is where PyInstaller puts most of the metadata and internal bookkeeping for building your executable. The default contents will look something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;build/
|
└── cli/
    ├── Analysis-00.toc
    ├── base_library.zip
    ├── COLLECT-00.toc
    ├── EXE-00.toc
    ├── PKG-00.pkg
    ├── PKG-00.toc
    ├── PYZ-00.pyz
    ├── PYZ-00.toc
    ├── warn-cli.txt
    └── xref-cli.html
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The build folder can be useful for debugging, but unless you have problems, this folder can largely be ignored. You&amp;rsquo;ll learn more about debugging later in this tutorial.&lt;/p&gt;
&lt;h3 id=&quot;dist-folder&quot;&gt;Dist Folder&lt;/h3&gt;
&lt;p&gt;After building, you&amp;rsquo;ll end up with a &lt;code&gt;dist/&lt;/code&gt; folder similar to the following:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;dist/
|
└── cli/
    └── cli
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;dist/&lt;/code&gt; folder contains the final artifact you&amp;rsquo;ll want to ship to your users. Inside the &lt;code&gt;dist/&lt;/code&gt; folder, there is a folder named after your entry-point. So in this example, you&amp;rsquo;ll have a &lt;code&gt;dist/cli&lt;/code&gt; folder that contains all the dependencies and executable for our application. The executable to run is &lt;code&gt;dist/cli/cli&lt;/code&gt; or &lt;code&gt;dist/cli/cli.exe&lt;/code&gt; if you&amp;rsquo;re on Windows.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll also find lots of files with the extension &lt;code&gt;.so&lt;/code&gt;, &lt;code&gt;.pyd&lt;/code&gt;, and &lt;code&gt;.dll&lt;/code&gt; depending on your Operating System. These are the shared libraries that represent the dependencies of your project that PyInstaller created and collected.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can add &lt;code&gt;*.spec&lt;/code&gt;, &lt;code&gt;build/&lt;/code&gt;, and &lt;code&gt;dist/&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file to keep &lt;code&gt;git
status&lt;/code&gt; clean if you&amp;rsquo;re using &lt;code&gt;git&lt;/code&gt; for version control. The &lt;a href=&quot;https://github.com/github/gitignore/blob/master/Python.gitignore&quot;&gt;default GitHub gitignore file for Python projects&lt;/a&gt; already does this for you.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You&amp;rsquo;ll want to distribute the entire &lt;code&gt;dist/cli&lt;/code&gt; folder, but you can rename &lt;code&gt;cli&lt;/code&gt; to anything that suits you.&lt;/p&gt;
&lt;p&gt;At this point you can try running the &lt;code&gt;dist/cli/cli&lt;/code&gt; executable if you&amp;rsquo;re following along with the feed reader example.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll notice that running the executable results in errors mentioning the &lt;code&gt;version.txt&lt;/code&gt; file. This is because the feed reader and its dependencies require some extra data files that PyInstaller doesn&amp;rsquo;t know about. To fix that, you&amp;rsquo;ll have to tell PyInstaller that &lt;code&gt;version.txt&lt;/code&gt; is required, which you&amp;rsquo;ll learn about when &lt;a href=&quot;#testing-your-new-executable&quot;&gt;testing your new executable&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;customizing-your-builds&quot;&gt;Customizing Your Builds&lt;/h2&gt;
&lt;p&gt;PyInstaller comes with lots of options that can be provided as spec files or normal CLI options. Below, you&amp;rsquo;ll find some of the most common and useful options.&lt;/p&gt;
&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;--name&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Change the name of your executable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a way to avoid your executable, spec file, and build artifact folders being named after your entry-point script. &lt;code&gt;--name&lt;/code&gt; is useful if you have a habit of naming your entry-point script something like &lt;code&gt;cli.py&lt;/code&gt;, as I do.&lt;/p&gt;
&lt;p&gt;You can build an executable called &lt;code&gt;realpython&lt;/code&gt; from the &lt;code&gt;cli.py&lt;/code&gt; script with a command like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py --name realpython
&lt;/pre&gt;&lt;/div&gt;

&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;--onefile&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Package your entire application into a single executable file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The default options create a folder of dependencies &lt;em&gt;and&lt;/em&gt; and executable, whereas &lt;code&gt;--onefile&lt;/code&gt; keeps distribution easier by creating &lt;em&gt;only&lt;/em&gt; an executable.&lt;/p&gt;
&lt;p&gt;This option takes no arguments. To bundle your project into a single file, you can build with a command like this:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py --onefile
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With the above command, your &lt;code&gt;dist/&lt;/code&gt; folder will only contain a single executable instead of a folder with all the dependencies in separate files.&lt;/p&gt;
&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;--hidden-import&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;List multiple top-level imports that PyInstaller was unable to detect automatically.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is one way to work around your code using &lt;code&gt;import&lt;/code&gt; inside functions and &lt;code&gt;__import__()&lt;/code&gt;. You can also use &lt;code&gt;--hidden-import&lt;/code&gt; multiple times in the same command.&lt;/p&gt;
&lt;p&gt;This option requires the name of the package that you want to include in your executable. For example, if your project imported the &lt;a href=&quot;https://realpython.com/python-requests/&quot;&gt;requests&lt;/a&gt; library inside of a function, then PyInstaller would not automatically include &lt;code&gt;requests&lt;/code&gt; in your executable. You could use the following command to force &lt;code&gt;requests&lt;/code&gt; to be included:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py --hiddenimport&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;requests
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can specify this multiple times in your build command, once for each hidden import.&lt;/p&gt;
&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;--add-data&lt;/code&gt; and &lt;code&gt;--add-binary&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Instruct PyInstaller to insert additional data or binary files into your build.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is useful when you want to bundle in configuration files, examples, or other non-code data. You&amp;rsquo;ll see an example of this later if you&amp;rsquo;re following along with the feed reader project.&lt;/p&gt;
&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;--exclude-module&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Exclude some modules from being included with your executable&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is useful to exclude developer-only requirements like testing frameworks. This is a great way to keep the artifact you give users as small as possible. For example, if you use &lt;a href=&quot;https://pypi.org/project/pytest/&quot;&gt;pytest&lt;/a&gt;, you may want to exclude this from your executable:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py --exclude-module&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pytest
&lt;/pre&gt;&lt;/div&gt;

&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;-w&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Avoid automatically opening a console window for &lt;code&gt;stdout&lt;/code&gt; logging.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is only useful if you&amp;rsquo;re building a GUI-enabled application. This helps your hide the details of your implementation by allowing users to never see a terminal.&lt;/p&gt;
&lt;p&gt;Similar to the &lt;code&gt;--onefile&lt;/code&gt; option, &lt;code&gt;-w&lt;/code&gt; takes no arguments:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py -w
&lt;/pre&gt;&lt;/div&gt;

&lt;p class=&quot;h4 mt-5&quot;&gt;&lt;code&gt;.spec file&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;As mentioned earlier, you can reuse the automatically generated &lt;code&gt;.spec&lt;/code&gt; file to further customize your executable. The &lt;code&gt;.spec&lt;/code&gt; file is a regular Python script that implicitly uses the PyInstaller library API.&lt;/p&gt;
&lt;p&gt;Since it&amp;rsquo;s a regular Python script, you can do almost anything inside of it. You can refer to the &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/spec-files.html&quot;&gt;official PyInstaller Spec file documentation&lt;/a&gt; for more information on that API.&lt;/p&gt;
&lt;h2 id=&quot;testing-your-new-executable&quot;&gt;Testing Your New Executable&lt;/h2&gt;
&lt;p&gt;The best way to test your new executable is on a new machine. The new machine should have the same OS as your build machine. Ideally, this machine should be as similar as possible to what your users use. That may not always be possible, so the next best thing is testing on your own machine.&lt;/p&gt;
&lt;p&gt;The key is to run the resulting executable &lt;em&gt;without&lt;/em&gt; your development environment activated. This means run without &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;conda&lt;/code&gt;, or any other &lt;strong&gt;environment&lt;/strong&gt; that can access your Python installation. Remember, one of the main goals of a PyInstaller-created executable is for users to not need anything installed on their machine.&lt;/p&gt;
&lt;p&gt;Picking up with the feed reader example, you&amp;rsquo;ll notice that running the default &lt;code&gt;cli&lt;/code&gt; executable in the &lt;code&gt;dist/cli&lt;/code&gt; folder fails. Luckily the error points you to the problem:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;FileNotFoundError: &amp;#39;version.txt&amp;#39; resource not found in &amp;#39;importlib_resources&amp;#39;&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[15110] Failed to execute script cli&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;importlib_resources&lt;/code&gt; package requires a &lt;code&gt;version.txt&lt;/code&gt; file. You can add this file to the build using the &lt;code&gt;--add-data&lt;/code&gt; option. Here&amp;rsquo;s an example of how to include the required &lt;code&gt;version.txt&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    --add-data venv/reader/lib/python3.6/site-packages/importlib_resources/version.txt:importlib_resources
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command tells PyInstaller to include the &lt;code&gt;version.txt&lt;/code&gt; file in the &lt;code&gt;importlib_resources&lt;/code&gt; folder in a new folder in your build called &lt;code&gt;importlib_resources&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;pyinstaller&lt;/code&gt; commands use the &lt;code&gt;\&lt;/code&gt; character to make the command easier to read. You can omit the &lt;code&gt;\&lt;/code&gt; when running commands on your own or copy and paste the commands as-is below provided you&amp;rsquo;re using the same paths.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You&amp;rsquo;ll want to adjust the path in the above command to match where you installed the feed reader dependencies.&lt;/p&gt;
&lt;p&gt;Now running the new executable will result in a new error about a &lt;code&gt;config.cfg&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;This file is required by the feed reader project, so you&amp;rsquo;ll need to make sure to include it in your build:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller cli.py &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    --add-data venv/reader/lib/python3.6/site-packages/importlib_resources/version.txt:importlib_resources &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    --add-data reader/config.cfg:reader
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, you&amp;rsquo;ll need to adjust the path to the file based on where you have the feed reader project.&lt;/p&gt;
&lt;p&gt;At this point, you should have a working executable that can be given directly to users!&lt;/p&gt;
&lt;h2 id=&quot;debugging-pyinstaller-executables&quot;&gt;Debugging PyInstaller Executables&lt;/h2&gt;
&lt;p&gt;As you saw above, you might encounter problems when running your executable. Depending on the complexity of your project, the fixes could be as simple as including data files like the feed reader example. However, sometimes you need more debugging techniques.&lt;/p&gt;
&lt;p&gt;Below are a few common strategies that are in no particular order. Often times one of these strategies or a combination will lead to a break-through in tough debugging sessions.&lt;/p&gt;
&lt;h3 id=&quot;use-the-terminal&quot;&gt;Use the Terminal&lt;/h3&gt;
&lt;p&gt;First, try running the executable from a terminal so you can see all the output.&lt;/p&gt;
&lt;p&gt;Remember to remove the &lt;code&gt;-w&lt;/code&gt; build flag to see all the &lt;code&gt;stdout&lt;/code&gt; in a console window. Often, you&amp;rsquo;ll see &lt;code&gt;ImportError&lt;/code&gt; exceptions if a dependency is missing.&lt;/p&gt;
&lt;h3 id=&quot;debug-files&quot;&gt;Debug Files&lt;/h3&gt;
&lt;p&gt;Inspect the &lt;code&gt;build/cli/warn-cli.txt&lt;/code&gt; file for any problems. PyInstaller creates &lt;em&gt;lots&lt;/em&gt; of output to help you understand exactly what it&amp;rsquo;s creating. Digging around in the &lt;code&gt;build/&lt;/code&gt; folder is a great place to start.&lt;/p&gt;
&lt;h3 id=&quot;single-directory-builds&quot;&gt;Single Directory Builds&lt;/h3&gt;
&lt;p&gt;Use the &lt;code&gt;--onedir&lt;/code&gt; distribution mode of creating distribution folder instead of a single executable. Again, this is the default mode. Building with &lt;code&gt;--onedir&lt;/code&gt; gives you the opportunity to inspect all the dependencies included instead of everything being hidden in a single executable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--onedir&lt;/code&gt; is useful for debugging, but &lt;code&gt;--onefile&lt;/code&gt; is typically easier for
users to comprehend. After debugging you may want to switch to &lt;code&gt;--onefile&lt;/code&gt; mode to simplify distribution.&lt;/p&gt;
&lt;h3 id=&quot;additional-cli-options&quot;&gt;Additional CLI Options&lt;/h3&gt;
&lt;p&gt;PyInstaller also has options to control the amount of information printed during the build process. Rebuild the executable with the &lt;code&gt;--log-level=DEBUG&lt;/code&gt; option to PyInstaller and review the output.&lt;/p&gt;
&lt;p&gt;PyInstaller will create &lt;em&gt;a lot&lt;/em&gt; of output when increasing the verbosity with &lt;code&gt;--log-level=DEBUG&lt;/code&gt;. It&amp;rsquo;s useful to save this output to a file you can refer to later instead of scrolling in your Terminal. To do this, you can use your shell&amp;rsquo;s &lt;a href=&quot;https://en.wikipedia.org/wiki/Redirection_(computing)&quot;&gt;redirection functionality&lt;/a&gt;. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pyinstaller --log-level&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;DEBUG cli.py &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&amp;gt; build.txt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By using the above command, you&amp;rsquo;ll have a file called &lt;code&gt;build.txt&lt;/code&gt; containing lots of additional &lt;code&gt;DEBUG&lt;/code&gt; messages.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The standard redirection with &lt;code&gt;&amp;gt;&lt;/code&gt; is not sufficient. PyInstaller prints to the &lt;code&gt;stderr&lt;/code&gt; stream, &lt;em&gt;not&lt;/em&gt; &lt;code&gt;stdout&lt;/code&gt;. This means you need to redirect the &lt;code&gt;stderr&lt;/code&gt; stream to a file, which can be done using a &lt;code&gt;2&lt;/code&gt; as in the previous command.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;rsquo;s a sample of what your &lt;code&gt;build.txt&lt;/code&gt; file might look like:&lt;/p&gt;
&lt;div class=&quot;highlight text&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;67 INFO: PyInstaller: 3.4
67 INFO: Python: 3.6.6
73 INFO: Platform: Darwin-18.2.0-x86_64-i386-64bit
74 INFO: wrote /Users/realpython/pyinstaller/reader/cli.spec
74 DEBUG: Testing for UPX ...
77 INFO: UPX is not available.
78 DEBUG: script: /Users/realptyhon/pyinstaller/reader/cli.py
78 INFO: Extending PYTHONPATH with paths
[&amp;#39;/Users/realpython/pyinstaller/reader&amp;#39;,
 &amp;#39;/Users/realpython/pyinstaller/reader&amp;#39;]
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This file will have a lot of detailed information about what was included in your build, why something was not included, and how the executable was packaged.&lt;/p&gt;
&lt;p&gt;You can also rebuild your executable using the &lt;code&gt;--debug&lt;/code&gt; option in addition to using the &lt;code&gt;--log-level&lt;/code&gt; option for even more information.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;-y&lt;/code&gt; and &lt;code&gt;--clean&lt;/code&gt; options are useful when rebuilding, especially when initially configuring your builds or building with &lt;a href=&quot;https://realpython.com/python-continuous-integration/&quot;&gt;Continuous Integration&lt;/a&gt;. These options remove old builds and omit the need for user input during the build process.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;additional-pyinstaller-docs&quot;&gt;Additional PyInstaller Docs&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/wiki&quot;&gt;PyInstaller GitHub Wiki&lt;/a&gt; has lots of useful links and debugging tips. Most notably are the sections on &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/wiki/How-to-Report-Bugs#make-sure-everything-is-packaged-correctly&quot;&gt;making sure everything is packaged correctly&lt;/a&gt; and what to do &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/wiki/If-Things-Go-Wrong&quot;&gt;if things go wrong&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;assisting-in-dependency-detection&quot;&gt;Assisting in Dependency Detection&lt;/h3&gt;
&lt;p&gt;The most common problem you&amp;rsquo;ll see is &lt;code&gt;ImportError&lt;/code&gt; exceptions if PyInstaller couldn&amp;rsquo;t properly detect all your dependencies. As mentioned before, this can happen if you&amp;rsquo;re using &lt;code&gt;__import__()&lt;/code&gt;, imports inside functions, or other types of &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/when-things-go-wrong.html?highlight=Hidden#listing-hidden-imports&quot;&gt;hidden imports&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Many of these types of problems can be resolved by using the &lt;code&gt;--hidden-import&lt;/code&gt; PyInstaller CLI option. This tells PyInstaller to include a module or package even if it doesn&amp;rsquo;t automatically detect it. This is the easiest way to work around lots of dynamic import magic in your application.&lt;/p&gt;
&lt;p&gt;Another way to work around problems is &lt;a href=&quot;https://pyinstaller.readthedocs.io/en/stable/hooks.html&quot;&gt;hook files&lt;/a&gt;. These files contain additional information to help PyInstaller package up a dependency. You can write your own hooks and tell PyInstaller to use them with the &lt;code&gt;--additional-hooks-dir&lt;/code&gt; CLI option.&lt;/p&gt;
&lt;p&gt;Hook files are how PyInstaller itself works internally so you can find lots of example hook files in the &lt;a href=&quot;https://github.com/pyinstaller/pyinstaller/tree/develop/PyInstaller/hooks&quot;&gt;PyInstaller source code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;PyInstaller is incredibly powerful, but it does have some limitations. Some of the limitations were discussed previously: hidden imports and relative imports in entry-point scripts.&lt;/p&gt;
&lt;p&gt;PyInstaller supports making executables for Windows, Linux, and macOS, but it cannot &lt;a href=&quot;https://en.wikipedia.org/wiki/Cross_compiler&quot;&gt;cross compile&lt;/a&gt;. Therefore, you cannot make an executable targeting one Operating System from another Operating System. So, to distribute executables for multiple types of OS, you&amp;rsquo;ll need a build machine for each supported OS.&lt;/p&gt;
&lt;p&gt;Related to the cross compile limitation, it&amp;rsquo;s useful to know that PyInstaller does not technically bundle absolutely everything your application needs to run. Your executable is still dependent on the users&amp;rsquo; &lt;a href=&quot;https://en.wikipedia.org/wiki/GNU_C_Library&quot;&gt;&lt;code&gt;glibc&lt;/code&gt;&lt;/a&gt;. Typically, you can work around the &lt;code&gt;glibc&lt;/code&gt; limitation by building on the oldest version of each OS you intend to target.&lt;/p&gt;
&lt;p&gt;For example, if you want to target a wide array of Linux machines, then you can build on an older version of &lt;a href=&quot;https://www.centos.org&quot;&gt;CentOS&lt;/a&gt;. This will give you compatibility with most versions newer than the one you build on. This is the same strategy described in &lt;a href=&quot;https://www.python.org/dev/peps/pep-0513/&quot;&gt;PEP 0513&lt;/a&gt; and is what the &lt;a href=&quot;https://www.pypa.io/en/latest/&quot;&gt;PyPA&lt;/a&gt; recommends for building compatible wheels.&lt;/p&gt;
&lt;p&gt;In fact, you might want to investigate using the &lt;a href=&quot;https://github.com/pypa/manylinux&quot;&gt;PyPA&amp;rsquo;s manylinux docker image&lt;/a&gt; for your Linux build environment. You could start with the base image then install PyInstaller along with all your dependencies and have a build image that supports most variants of Linux.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;PyInstaller can help make complicated installation documents unnecessary. Instead, your users can simply run your executable to get started as quickly as possible. The PyInstaller workflow can be summed up by doing the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an entry-point script that calls your main function.&lt;/li&gt;
&lt;li&gt;Install PyInstaller.&lt;/li&gt;
&lt;li&gt;Run PyInstaller on your entry-point.&lt;/li&gt;
&lt;li&gt;Test your new executable.&lt;/li&gt;
&lt;li&gt;Ship your resulting &lt;code&gt;dist/&lt;/code&gt; folder to users.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your users don&amp;rsquo;t have to know what version of Python you used or that your application uses Python at all!&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Introduction to Python Exceptions</title>
      <id>https://realpython.com/courses/introduction-python-exceptions/</id>
      <link href="https://realpython.com/courses/introduction-python-exceptions/"/>
      <updated>2019-03-05T23:39:23+00:00</updated>
      <summary>Learn what exceptions are good for in Python. You&#39;ll see how to raise exceptions and how to handle them with &quot;try/except&quot; blocks.</summary>
      <content type="html">
        &lt;p&gt;A Python program terminates as soon as it encounters an error. In Python, an error can be a syntax error or an exception. In this course, you will see what an exception is and how it differs from a syntax error. After that, you will learn about raising exceptions and making assertions. Then, you&amp;rsquo;ll finish with a demonstration of the try and except block.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Writing Beautiful Pythonic Code With PEP 8</title>
      <id>https://realpython.com/courses/writing-beautiful-python-code-pep-8/</id>
      <link href="https://realpython.com/courses/writing-beautiful-python-code-pep-8/"/>
      <updated>2019-03-05T18:51:13+00:00</updated>
      <summary>Learn how to write high-quality, readable code by using the Python style guidelines laid out in PEP 8. Following these guidelines helps you make a great impression when sharing your work with potential employers and collaborators. This course outlines the key guidelines laid out in PEP 8. It&#39;s aimed at beginner to intermediate programmers.</summary>
      <content type="html">
        &lt;p&gt;Learn how to write high-quality, readable code by using the Python style guidelines laid out in PEP 8. Following these guidelines helps you make a great impression when sharing your work with potential employers and collaborators.&lt;/p&gt;
&lt;p&gt;This course outlines the key guidelines laid out in PEP 8. It&amp;rsquo;s aimed at beginner to intermediate programmers.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python 3&#39;s f-Strings: An Improved String Formatting Syntax</title>
      <id>https://realpython.com/courses/python-3-f-strings-improved-string-formatting-syntax/</id>
      <link href="https://realpython.com/courses/python-3-f-strings-improved-string-formatting-syntax/"/>
      <updated>2019-03-05T18:49:19+00:00</updated>
      <summary>As of Python 3.6, f-strings are a great new way to format strings. Not only are they more readable, more concise, and less prone to error than other ways of formatting, but they are also faster! By the end of this course, you&#39;ll know how and why to start using f-strings today.</summary>
      <content type="html">
        &lt;p&gt;As of Python 3.6, f-strings are a great new way to format strings. Not only are they more readable, more concise, and less prone to error than other ways of formatting, but they are also faster! By the end of this course, you&amp;rsquo;ll know how and why to start using f-strings today.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Refactoring Python Applications for Simplicity</title>
      <id>https://realpython.com/python-refactoring/</id>
      <link href="https://realpython.com/python-refactoring/"/>
      <updated>2019-03-04T14:00:00+00:00</updated>
      <summary>In this step-by-step tutorial, you&#39;ll learn how to refactor your Python application to be simpler and more maintainable and have fewer bugs. You&#39;ll cover code metrics, refactoring tools, and common anti-patterns.</summary>
      <content type="html">
        &lt;p&gt;Do you want simpler Python code? You always start a project with the best intentions, a clean codebase, and a nice structure. But over time, there are changes to your apps, and things can get a little messy.&lt;/p&gt;
&lt;p&gt;If you can write and maintain clean, simple Python code, then it&amp;rsquo;ll save you lots of time in the long term. You can spend less time testing, finding bugs, and making changes when your code is well laid out and simple to follow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to measure the complexity of Python code and your applications&lt;/li&gt;
&lt;li&gt;How to change your code without breaking it&lt;/li&gt;
&lt;li&gt;What the common issues in Python code that cause extra complexity are and how you can fix them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Throughout this tutorial, I&amp;rsquo;m going to use the theme of subterranean railway networks to explain complexity because navigating a subway system in a large city can be complicated! Some are well designed, and others seem overly complex.&lt;/p&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-mastery-course&quot; data-focus=&quot;false&quot;&gt;5 Thoughts On Python Mastery&lt;/a&gt;, a free course for Python developers that shows you the roadmap and the mindset you&#39;ll need to take your Python skills to the next level.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;code-complexity-in-python&quot;&gt;Code Complexity in Python&lt;/h2&gt;
&lt;p&gt;The complexity of an application and its codebase is relative to the task it&amp;rsquo;s performing. If you&amp;rsquo;re writing code for NASA&amp;rsquo;s jet propulsion laboratory (literally &lt;a href=&quot;https://github.com/chrislgarry/Apollo-11&quot;&gt;rocket science&lt;/a&gt;), then it&amp;rsquo;s going to be complicated.&lt;/p&gt;
&lt;p&gt;The question isn&amp;rsquo;t so much, &amp;ldquo;Is my code complicated?&amp;rdquo; as, &amp;ldquo;Is my code more complicated than it needs to be?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The Tokyo railway network is one of the most extensive and complicated in the world. This is partly because Tokyo is a metropolis of over 30 million people, but it&amp;rsquo;s also because there are 3 networks overlapping each other.&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Image_from_iOS.66044b21299a.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-50&quot; src=&quot;https://files.realpython.com/media/Image_from_iOS.66044b21299a.jpg&quot; width=&quot;2005&quot; height=&quot;2005&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Image_from_iOS.66044b21299a.jpg&amp;amp;w=501&amp;amp;sig=6c7c878db998c2a237f9f9fca6ffc7b38e995b9c 501w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Image_from_iOS.66044b21299a.jpg&amp;amp;w=1002&amp;amp;sig=4ea2efaf0619420e659c134b3db436cbad6eb586 1002w, https://files.realpython.com/media/Image_from_iOS.66044b21299a.jpg 2005w&quot; sizes=&quot;75vw&quot; alt=&quot;Anthony Shaw on the Tokyo Metro&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The author of this article getting lost on the Tokyo Metro&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;There are the Toei and Tokyo Metro rapid-transport networks as well as the Japan Rail East trains going through Central Tokyo. To even the most experienced traveler, navigating central Tokyo can be mind-bogglingly complicated.&lt;/p&gt;
&lt;p&gt;Here is a map of the Tokyo railway network to give you some perspective:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/img_01.c19c0b4f6df2.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/img_01.c19c0b4f6df2.png&quot; width=&quot;950&quot; height=&quot;673&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/img_01.c19c0b4f6df2.png&amp;amp;w=237&amp;amp;sig=7db4c3c33076c2d57342f3ad1653f569ac8e56b2 237w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/img_01.c19c0b4f6df2.png&amp;amp;w=475&amp;amp;sig=1163829c74df9d979290c97db535c4357649aece 475w, https://files.realpython.com/media/img_01.c19c0b4f6df2.png 950w&quot; sizes=&quot;75vw&quot; alt=&quot;Map of the Tokyo railway network&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Image: &lt;a href=&quot;https://www.tokyometro.jp/en/subwaymap/&quot; target=&quot;_blank&quot;&gt;Tokyo Metro Co.&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If your code is starting to look a bit like this map, then this is the tutorial for you. &lt;/p&gt;
&lt;p&gt;First, we&amp;rsquo;ll go through 4 metrics of complexity that can give you a scale to measure your relative progress in the mission to make your code simpler:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/content_nav_graphic-02.3b9543d9d7b6.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/content_nav_graphic-02.3b9543d9d7b6.png&quot; width=&quot;1667&quot; height=&quot;522&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-02.3b9543d9d7b6.png&amp;amp;w=416&amp;amp;sig=eb2e638f6f3573ddae823711d33be3ee3ecae403 416w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-02.3b9543d9d7b6.png&amp;amp;w=833&amp;amp;sig=806d43d42b83f70f59b36ba1d5260dfbd9a00c40 833w, https://files.realpython.com/media/content_nav_graphic-02.3b9543d9d7b6.png 1667w&quot; sizes=&quot;75vw&quot; alt=&quot;Code Complexity Metrics Table of Contents&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After you&amp;rsquo;ve explored the metrics, you&amp;rsquo;ll learn about a tool called &lt;code&gt;wily&lt;/code&gt; to automate calculating those metrics.&lt;/p&gt;
&lt;h3 id=&quot;metrics-for-measuring-complexity&quot;&gt;Metrics for Measuring Complexity&lt;/h3&gt;
&lt;p&gt;Much time and research have been put into analyzing the complexity of computer software. Overly complex and unmaintainable applications can have a very real cost. &lt;/p&gt;
&lt;p&gt;The complexity of software correlates to the quality. Code that is easy to read and understand is more likely to be updated by developers in the future.&lt;/p&gt;
&lt;p&gt;Here are some metrics for programming languages. They apply to many languages, not just Python.&lt;/p&gt;
&lt;h4 id=&quot;lines-of-code&quot;&gt;Lines of Code&lt;/h4&gt;
&lt;p&gt;LOC, or Lines of Code, is the crudest measure of complexity. It is debatable whether there is any direct correlation between the lines of code and the complexity of an application, but the indirect correlation is clear. After all, a program with 5 lines is likely simpler than one with 5 million.&lt;/p&gt;
&lt;p&gt;When looking at Python metrics, we try to ignore blank lines and lines containing comments.&lt;/p&gt;
&lt;p&gt;Lines of code can be calculated using the &lt;code&gt;wc&lt;/code&gt; command on Linux and Mac OS, where &lt;code&gt;file.py&lt;/code&gt; is the name of the file you want to measure:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wc -l file.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you want to add the combined lines in a folder by recursively searching for all &lt;code&gt;.py&lt;/code&gt; files, you can combine &lt;code&gt;wc&lt;/code&gt; with the &lt;code&gt;find&lt;/code&gt; command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; find . -name &lt;span class=&quot;se&quot;&gt;\*&lt;/span&gt;.py &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; xargs wc -l
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For Windows, PowerShell offers a word count command in &lt;code&gt;Measure-Object&lt;/code&gt; and a recursive file search in &lt;code&gt;Get-ChildItem&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight powershell&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-Path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;py&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;-Recurse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Measure-Object&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;–&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the response, you will see the total number of lines.&lt;/p&gt;
&lt;p&gt;Why are lines of code used to quantify the amount of code in your application? The assumption is that a line of code roughly equates to a statement. Lines is a better measure than characters, which would include whitespace. &lt;/p&gt;
&lt;p&gt;In Python, we are encouraged to put a single statement on each line. This example is 9 lines of code:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Enter a number: &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 4 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 5 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{x}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is less than &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{y}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 6 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 7 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{x}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is equal to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{y}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 8 &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 9 &lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{x}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is more than &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{y}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you used only lines of code as your measure of complexity, it could encourage the wrong behaviors. &lt;/p&gt;
&lt;p&gt;Python code should be easy to read and understand. Taking that last example, you could reduce the number of lines of code to 3:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;lineno&quot;&gt; 1 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Enter a number:&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 2 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;equality&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;is equal to&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;is less than&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;is more than&amp;quot;&lt;/span&gt;
&lt;span class=&quot;lineno&quot;&gt; 3 &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{x}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{equality}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{y}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But the result is hard to read, and PEP 8 has guidelines around maximum line length and line breaking. You can check out &lt;a href=&quot;https://realpython.com/python-pep8/&quot;&gt;How to Write Beautiful Python Code With PEP 8&lt;/a&gt; for more on PEP 8.&lt;/p&gt;
&lt;p&gt;This code block uses 2 Python language features to make the code shorter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Compound statements:&lt;/strong&gt; using &lt;code&gt;;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chained conditional or ternary statements:&lt;/strong&gt; &lt;code&gt;name = value if condition else value if condition2 else value2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have reduced the number of lines of code but violated one of the fundamental laws of Python:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Readability counts&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; Tim Peters, Zen of Python&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This shortened code is potentially harder to maintain because code maintainers are humans, and this short code is harder to read. We will explore some more advanced and useful metrics for complexity.&lt;/p&gt;
&lt;h4 id=&quot;cyclomatic-complexity&quot;&gt;Cyclomatic Complexity&lt;/h4&gt;
&lt;p&gt;Cyclomatic complexity is the measure of how many independent code paths there are through your application. A path is a sequence of statements that the interpreter can follow to get to the end of the application.&lt;/p&gt;
&lt;p&gt;One way to think of cyclomatic complexity and code paths is imagine your code is like a railway network.&lt;/p&gt;
&lt;p&gt;For a journey, you may need to change trains to reach your destination. The Lisbon Metropolitan railway system in Portugal is simple and easy to navigate. The cyclomatic complexity for any trip is equal to the number of lines you need to travel on:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Plan-du-Metro-de-Lisbonne.1ad8d99b3853.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/Plan-du-Metro-de-Lisbonne.1ad8d99b3853.png&quot; width=&quot;1072&quot; height=&quot;746&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Plan-du-Metro-de-Lisbonne.1ad8d99b3853.png&amp;amp;w=268&amp;amp;sig=a73d06c444ffe8400f1cdc0dd6188ef4fdc3294b 268w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Plan-du-Metro-de-Lisbonne.1ad8d99b3853.png&amp;amp;w=536&amp;amp;sig=3162c549511c2095449d33c8fece3e64b2592c2d 536w, https://files.realpython.com/media/Plan-du-Metro-de-Lisbonne.1ad8d99b3853.png 1072w&quot; sizes=&quot;75vw&quot; alt=&quot;Map to Lisbon Subway map&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Image: &lt;a href=&quot;https://www.metrolisboa.pt/en/travel/diagrams-and-maps/&quot; target=&quot;_blank&quot;&gt;Metro Lisboa&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;If you needed to get from &lt;em&gt;Alvalade&lt;/em&gt; to &lt;em&gt;Anjos&lt;/em&gt;, then you would travel 5 stops on the &lt;em&gt;linha verde&lt;/em&gt; (green line):&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/lisbon-subway-map-1.f5d3b712274c.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/lisbon-subway-map-1.f5d3b712274c.png&quot; width=&quot;1072&quot; height=&quot;746&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/lisbon-subway-map-1.f5d3b712274c.png&amp;amp;w=268&amp;amp;sig=940b49580ad1aaed5c295371c060f5818289de65 268w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/lisbon-subway-map-1.f5d3b712274c.png&amp;amp;w=536&amp;amp;sig=73636d3bdcccd4aa494d792836403cb1796072e8 536w, https://files.realpython.com/media/lisbon-subway-map-1.f5d3b712274c.png 1072w&quot; sizes=&quot;75vw&quot; alt=&quot;Map to Lisbon Subway map&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Image: &lt;a href=&quot;https://www.metrolisboa.pt/en/travel/diagrams-and-maps/&quot; target=&quot;_blank&quot;&gt;Metro Lisboa&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This trip has a cyclomatic complexity of 1 because you only take 1 train. It&amp;rsquo;s an easy trip. That train is equivalent in this analogy to a code branch.&lt;/p&gt;
&lt;p&gt;If you needed to travel from the &lt;em&gt;Aeroporto&lt;/em&gt; (airport) to sample the &lt;a href=&quot;https://pasteisdebelem.pt/en/&quot;&gt;food in the district of &lt;em&gt;Belém&lt;/em&gt;&lt;/a&gt;, then it&amp;rsquo;s a more complicated journey. You would have to change trains at &lt;em&gt;Alameda&lt;/em&gt; and &lt;em&gt;Cais do Sodré&lt;/em&gt;: &lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/lisbon-subway-map-2.ac4c65844174.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/lisbon-subway-map-2.ac4c65844174.png&quot; width=&quot;1072&quot; height=&quot;746&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/lisbon-subway-map-2.ac4c65844174.png&amp;amp;w=268&amp;amp;sig=b6aa30d67c08743120f766f0d2f9d1d463f55a65 268w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/lisbon-subway-map-2.ac4c65844174.png&amp;amp;w=536&amp;amp;sig=a20992d7286afa0bf6c3805a54eea4c2bfc8417c 536w, https://files.realpython.com/media/lisbon-subway-map-2.ac4c65844174.png 1072w&quot; sizes=&quot;75vw&quot; alt=&quot;Map to Lisbon Subway map&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Image: &lt;a href=&quot;https://www.metrolisboa.pt/en/travel/diagrams-and-maps/&quot; target=&quot;_blank&quot;&gt;Metro Lisboa&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This trip has a cyclomatic complexity of 3, because you take 3 trains. You might be better off taking a taxi!&lt;/p&gt;
&lt;p&gt;Seeing as how you&amp;rsquo;re not navigating Lisbon, but rather writing code, the changes of train line become a branch in execution, like an &lt;code&gt;if&lt;/code&gt; statement.
Let&amp;rsquo;s explore this example:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is only 1 way this code can be executed, so it has a cyclomatic complexity of 1.&lt;/p&gt;
&lt;p&gt;If we add a decision, or branch to the code as an &lt;code&gt;if&lt;/code&gt; statement, it increases the complexity:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though there is only 1 way this code can be executed, as &lt;code&gt;x&lt;/code&gt; is a constant, this has a cyclomatic complexity of 2.  All of the cyclomatic complexity analyzers will treat an &lt;code&gt;if&lt;/code&gt; statement as a branch.&lt;/p&gt;
&lt;p&gt;This is also an example of overly complex code. The &lt;code&gt;if&lt;/code&gt; statement is useless as &lt;code&gt;x&lt;/code&gt; has a fixed value. You could simply refactor this example to the following:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That was a toy example, so let&amp;rsquo;s explore something a little more real.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main()&lt;/code&gt; has a cyclomatic complexity of 5. I&amp;rsquo;ll comment each branch in the code so you can see where they are:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# cyclomatic_example.py&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 1&lt;/span&gt;
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Provide a file path&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hll&quot;&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 2&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filepath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 3&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readlines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 4&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;hll&quot;&gt;                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 5&lt;/span&gt;
&lt;/span&gt;                    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vm&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Ignored.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are certainly ways that code can be refactored into a far simpler alternative. We&amp;rsquo;ll get to that later.&lt;/p&gt;
&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Cyclomatic Complexity measure was &lt;a href=&quot;http://www.literateprogramming.com/mccabe.pdf&quot;&gt;developed by Thomas J. McCabe, Sr&lt;/a&gt; in 1976. You may see it referred to as the &lt;strong&gt;McCabe metric&lt;/strong&gt; or &lt;strong&gt;McCabe number&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In the following examples, we will use the &lt;a href=&quot;https://radon.readthedocs.io/&quot;&gt;&lt;code&gt;radon&lt;/code&gt;&lt;/a&gt; library &lt;a href=&quot;https://pypi.org/project/radon&quot;&gt;from PyPi&lt;/a&gt; to calculate metrics. You can install it now:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install radon
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To calculate cyclomatic complexity using &lt;code&gt;radon&lt;/code&gt;, you can save the example into a file called &lt;code&gt;cyclomatic_example.py&lt;/code&gt; and use &lt;code&gt;radon&lt;/code&gt; from the command line.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;radon&lt;/code&gt; command takes 2 main arguments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The type of analysis (&lt;code&gt;cc&lt;/code&gt; for cyclomatic complexity)&lt;/li&gt;
&lt;li&gt;A path to the file or folder to analyze&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Execute the &lt;code&gt;radon&lt;/code&gt; command with the &lt;code&gt;cc&lt;/code&gt; analysis against the &lt;code&gt;cyclomatic_example.py&lt;/code&gt; file. Adding &lt;code&gt;-s&lt;/code&gt; will give the cyclomatic complexity in the output:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; radon cc cyclomatic_example.py -s
&lt;span class=&quot;go&quot;&gt;cyclomatic_example.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    F 4:0 main - B (6)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The output is a little cryptic. Here is what each part means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;F&lt;/code&gt; means function, &lt;code&gt;M&lt;/code&gt; means method, and &lt;code&gt;C&lt;/code&gt; means class.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt; is the name of the function.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;4&lt;/code&gt; is the line the function starts on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;B&lt;/code&gt; is the rating from A to F. A is the best grade, meaning the least complexity.&lt;/li&gt;
&lt;li&gt;The number in parentheses, &lt;code&gt;6&lt;/code&gt;, is the cyclomatic complexity of the code. &lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;halstead-metrics&quot;&gt;Halstead Metrics&lt;/h4&gt;
&lt;p&gt;The Halstead complexity metrics relate to the size of a program&amp;rsquo;s codebase. They were developed by Maurice H. Halstead in 1977. There are 4 measures in the Halstead equations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Operands&lt;/strong&gt; are values and names of variables.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Operators&lt;/strong&gt; are all of the built-in keywords, like &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt; or &lt;code&gt;while&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length (N)&lt;/strong&gt; is the number of operators plus the number of operands in your program.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vocabulary (h)&lt;/strong&gt; is the number of &lt;em&gt;unique&lt;/em&gt; operators plus the number of &lt;em&gt;unique&lt;/em&gt; operands in your a program.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are then 3 additional metrics with those measures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Volume (V)&lt;/strong&gt; represents a product of the &lt;strong&gt;length&lt;/strong&gt; and the &lt;strong&gt;vocabulary&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Difficulty (D)&lt;/strong&gt; represents a product of half the unique operands and the reuse of operands.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Effort (E)&lt;/strong&gt; is the overall metric that is a product of &lt;strong&gt;volume&lt;/strong&gt; and &lt;strong&gt;difficulty&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this is very abstract, so let&amp;rsquo;s put it in relative terms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The effort of your application is highest if you use a lot of operators and unique operands.&lt;/li&gt;
&lt;li&gt;The effort of your application is lower if you use a few operators and fewer variables.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the &lt;code&gt;cyclomatic_complexity.py&lt;/code&gt; example, operators and operands both occur on the first line:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# import (operator), sys (operand)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;import&lt;/code&gt; is an operator, and &lt;code&gt;sys&lt;/code&gt; is the name of the module, so it&amp;rsquo;s an operand.&lt;/p&gt;
&lt;p&gt;In a slightly more complex example, there are a number of operators and operands:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are 5 operators in this example: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;if&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Furthermore, there are 2 operands: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;sys.argv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Be aware that &lt;code&gt;radon&lt;/code&gt; only counts a subset of operators. For example, parentheses are excluded in any calculations. &lt;/p&gt;
&lt;p&gt;To calculate the Halstead measures in &lt;code&gt;radon&lt;/code&gt;, you can run the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; radon hal cyclomatic_example.py
&lt;span class=&quot;go&quot;&gt;cyclomatic_example.py:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    h1: 3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    h2: 6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    N1: 3&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    N2: 6&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    vocabulary: 9&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    length: 9&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    calculated_length: 20.264662506490406&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    volume: 28.529325012980813&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    difficulty: 1.5&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    effort: 42.793987519471216&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    time: 2.377443751081734&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    bugs: 0.009509775004326938&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Why does &lt;code&gt;radon&lt;/code&gt; give a metric for time and bugs?&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;Halstead theorized that you could estimate the time taken in seconds to code by dividing the effort (&lt;code&gt;E&lt;/code&gt;) by 18.&lt;/p&gt;
&lt;p&gt;Halstead also stated that the expected number of bugs could be estimated dividing the volume (&lt;code&gt;V&lt;/code&gt;) by 3000. Keep in mind this was written in 1977, before Python was even invented! So don&amp;rsquo;t panic and start looking for bugs just yet.&lt;/p&gt;
&lt;/div&gt;
&lt;h4 id=&quot;maintainability-index&quot;&gt;Maintainability Index&lt;/h4&gt;
&lt;p&gt;The maintainability index brings the McCabe Cyclomatic Complexity and the Halstead Volume measures in a scale roughly between zero and one-hundred.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested, the original equation is as follows: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/latex_d85438b40ae200b03ca93fef1cdd375a.0b70e9ab2442.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/latex_d85438b40ae200b03ca93fef1cdd375a.0b70e9ab2442.png&quot; width=&quot;1598&quot; height=&quot;83&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/latex_d85438b40ae200b03ca93fef1cdd375a.0b70e9ab2442.png&amp;amp;w=399&amp;amp;sig=a60a97942b2b68abf15c0ff7be87e9b386a071fb 399w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/latex_d85438b40ae200b03ca93fef1cdd375a.0b70e9ab2442.png&amp;amp;w=799&amp;amp;sig=4177d321b40306bb892dbcfb8fd40c2ba68bf0e5 799w, https://files.realpython.com/media/latex_d85438b40ae200b03ca93fef1cdd375a.0b70e9ab2442.png 1598w&quot; sizes=&quot;75vw&quot; alt=&quot;MI Equation&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the equation, &lt;code&gt;V&lt;/code&gt; is the Halstead volume metric, &lt;code&gt;C&lt;/code&gt; is the cyclomatic complexity, and &lt;code&gt;L&lt;/code&gt; is the number of lines of code.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re as baffled as I was when I first saw this equation, here&amp;rsquo;s it means: it calculates a scale that includes the number of variables, operations, decision paths, and lines of code.&lt;/p&gt;
&lt;p&gt;It is used across many tools and languages, so it&amp;rsquo;s one of the more standard metrics. However, there are numerous revisions of the equation, so the exact number shouldn&amp;rsquo;t be taken as fact. &lt;code&gt;radon&lt;/code&gt;, &lt;code&gt;wily&lt;/code&gt;, and Visual Studio cap the number between 0 and 100.&lt;/p&gt;
&lt;p&gt;On the maintainability index scale, all you need to be paying attention to is when your code is getting significantly lower (toward 0). The scale considers anything lower than 25 as &lt;strong&gt;hard to maintain&lt;/strong&gt;, and anything over 75 as &lt;strong&gt;easy to maintain&lt;/strong&gt;. The Maintainability Index is also referred to as &lt;strong&gt;MI&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The maintainability index can be used as a measure to get the current maintainability of your application and see if you&amp;rsquo;re making progress as you refactor it.&lt;/p&gt;
&lt;p&gt;To calculate the maintainability index from &lt;code&gt;radon&lt;/code&gt;, run the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; radon mi cyclomatic_example.py -s
&lt;span class=&quot;go&quot;&gt;cyclomatic_example.py - A (87.42)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this result, &lt;code&gt;A&lt;/code&gt; is the grade that &lt;code&gt;radon&lt;/code&gt; has applied to the number &lt;code&gt;87.42&lt;/code&gt; on a scale. On this scale, &lt;code&gt;A&lt;/code&gt; is most maintainable and &lt;code&gt;F&lt;/code&gt; the least.&lt;/p&gt;
&lt;h3 id=&quot;using-wily-to-capture-and-track-your-projects-complexity&quot;&gt;Using &lt;code&gt;wily&lt;/code&gt; to Capture and Track Your Projects&amp;rsquo; Complexity&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/tonybaloney/wily&quot;&gt;&lt;code&gt;wily&lt;/code&gt; is an open-source software project&lt;/a&gt; for collecting code-complexity metrics, including the ones we&amp;rsquo;ve covered so far like Halstead, Cyclomatic, and LOC. &lt;code&gt;wily&lt;/code&gt; integrates with Git and can automate the collection of metrics across Git branches and revisions.&lt;/p&gt;
&lt;p&gt;The purpose of &lt;code&gt;wily&lt;/code&gt; is to give you the ability to see trends and changes in the complexity of your code over time. If you were trying to fine-tune a car or improve your fitness, you&amp;rsquo;d start off with measuring a baseline and tracking improvements over time.&lt;/p&gt;
&lt;h4 id=&quot;installing-wily&quot;&gt;Installing &lt;code&gt;wily&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;wily&lt;/code&gt; is available &lt;a href=&quot;https://pypi.org/project/wily/&quot;&gt;on PyPi&lt;/a&gt; and can be installed using pip:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install wily
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once &lt;code&gt;wily&lt;/code&gt; is installed, you have some commands available in your command-line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;wily build&lt;/code&gt;:&lt;/strong&gt; iterate through the Git history and analyze the metrics for each file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;wily report&lt;/code&gt;:&lt;/strong&gt; see the historical trend in metrics for a given file or folder&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;wily graph&lt;/code&gt;:&lt;/strong&gt; graph a set of metrics in an HTML file&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;building-a-cache&quot;&gt;Building a Cache&lt;/h4&gt;
&lt;p&gt;Before you can use &lt;code&gt;wily&lt;/code&gt;, you need to analyze your project. This is done using the &lt;code&gt;wily build&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;For this section of the tutorial, we will analyze the very popular &lt;code&gt;requests&lt;/code&gt; package, used for talking to HTTP APIs. Because this project is open-source and available on GitHub, we can easily access and download a copy of the source code:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git clone https://github.com/requests/requests
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; requests
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; ls
&lt;span class=&quot;go&quot;&gt;AUTHORS.rst        CONTRIBUTING.md    LICENSE            Makefile&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Pipfile.lock       _appveyor          docs               pytest.ini&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;setup.cfg          tests              CODE_OF_CONDUCT.md HISTORY.md&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;MANIFEST.in        Pipfile            README.md          appveyor.yml&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;ext                requests           setup.py           tox.ini&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&quot;alert alert-primary&quot; role=&quot;alert&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Windows users should use the PowerShell command prompt for the following examples instead of traditional MS-DOS Command-Line. To start the PowerShell CLI press &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-windows&quot;&gt;Win&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-r&quot;&gt;R&lt;/kbd&gt;&lt;/span&gt; and type &lt;code&gt;powershell&lt;/code&gt; then &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-enter&quot;&gt;Enter&lt;/kbd&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You will see a number of folders here, for tests, documentation, and configuration. We&amp;rsquo;re only interested in the source code for the &lt;code&gt;requests&lt;/code&gt; Python package, which is in a folder called &lt;code&gt;requests&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Call the &lt;code&gt;wily build&lt;/code&gt; command from the cloned source code and provide the name of the source code folder as the first argument:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily build requests
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will take a few minutes to analyze, depending on how much CPU power your computer has: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/capture1.b776bd67d0b9.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/capture1.b776bd67d0b9.gif&quot; width=&quot;1276&quot; height=&quot;257&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/capture1.b776bd67d0b9.gif&amp;amp;w=319&amp;amp;sig=4f83c41a32803b4076856d1f91fe77b69d447ee5 319w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/capture1.b776bd67d0b9.gif&amp;amp;w=638&amp;amp;sig=3cd178945e277c5be741eb2c50ab0d1f46676b18 638w, https://files.realpython.com/media/capture1.b776bd67d0b9.gif 1276w&quot; sizes=&quot;75vw&quot; alt=&quot;Screenshot capture of Wily build command&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;collecting-data-on-your-project&quot;&gt;Collecting Data on Your Project&lt;/h4&gt;
&lt;p&gt;Once you have analyzed the &lt;code&gt;requests&lt;/code&gt; source code, you can query any file or folder to see key metrics. Earlier in the tutorial, we discussed the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lines of Code&lt;/li&gt;
&lt;li&gt;Maintainability Index&lt;/li&gt;
&lt;li&gt;Cyclomatic Complexity&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are the 3 default metrics in &lt;code&gt;wily&lt;/code&gt;. To see those metrics for a specific file (such as &lt;code&gt;requests/api.py&lt;/code&gt;), run the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily report requests/api.py
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;wily&lt;/code&gt; will print a tabular report on the default metrics for each Git commit in reverse date order. You will see the most recent commit at the top and the oldest at the bottom:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;
&lt;table class=&quot;table table-hover&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th class=&quot;text-left&quot;&gt;Revision&lt;/th&gt;
&lt;th class=&quot;text-left&quot;&gt;Author&lt;/th&gt;
&lt;th class=&quot;text-left&quot;&gt;Date&lt;/th&gt;
&lt;th class=&quot;text-left&quot;&gt;MI&lt;/th&gt;
&lt;th class=&quot;text-left&quot;&gt;Lines of Code&lt;/th&gt;
&lt;th class=&quot;text-left&quot;&gt;Cyclomatic Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;f37daf2&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Nate Prewitt&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2019-01-13&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;6dd410f&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Ofek Lev&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2019-01-13&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;5c1f72e&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Nate Prewitt&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2018-12-14&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;c4d7680&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Matthieu Moy&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2018-12-14&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;c452e3b&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Nate Prewitt&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2018-12-11&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td class=&quot;text-left&quot;&gt;5a1e738&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;Nate Prewitt&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;2018-12-10&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;100 (0.0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;158 (0)&lt;/td&gt;
&lt;td class=&quot;text-left&quot;&gt;9 (0)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;This tells us that the &lt;code&gt;requests/api.py&lt;/code&gt; file has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;158 lines of code&lt;/li&gt;
&lt;li&gt;A perfect maintainability index of 100&lt;/li&gt;
&lt;li&gt;A cyclomatic complexity of 9&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see other metrics, you first need to know the names of them. You can see this by running the following command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily list-metrics
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will see a list of operators, modules that analyze the code, and the metrics they provide.&lt;/p&gt;
&lt;p&gt;To query alternative metrics on the report command, add their names after the filename. You can add as many metrics as you wish. Here&amp;rsquo;s an example with the Maintainability Rank and the Source Lines of Code:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily report requests/api.py maintainability.rank raw.sloc
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You will see the table now has 2 different columns with the alternative metrics.&lt;/p&gt;
&lt;h4 id=&quot;graphing-metrics&quot;&gt;Graphing Metrics&lt;/h4&gt;
&lt;p&gt;Now that you know the names of the metrics and how to query them on the command line, you can also visualize them in graphs. &lt;code&gt;wily&lt;/code&gt; supports HTML and interactive charts with a similar interface as the report command:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily graph requests/sessions.py maintainability.mi
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your default browser will open with an interactive chart like this: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/capture2.61e3fed99791.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block border &quot; src=&quot;https://files.realpython.com/media/capture2.61e3fed99791.gif&quot; width=&quot;1276&quot; height=&quot;860&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/capture2.61e3fed99791.gif&amp;amp;w=319&amp;amp;sig=5ed7af0a1675f055cc89f409040f7b00b53f7712 319w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/capture2.61e3fed99791.gif&amp;amp;w=638&amp;amp;sig=16ad5535f7b9ca843c2a2d0cd4c1c105737c5f68 638w, https://files.realpython.com/media/capture2.61e3fed99791.gif 1276w&quot; sizes=&quot;75vw&quot; alt=&quot;Screenshot capture of Wily graph command&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can hover over specific data points, and it will show the Git commit message as well as the data.&lt;/p&gt;
&lt;p&gt;If you want to save the HTML file in a folder or repository, you can add the &lt;code&gt;-o&lt;/code&gt; flag with the path to a file:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily graph requests/sessions.py maintainability.mi -o my_report.html
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There will now be a file called &lt;code&gt;my_report.html&lt;/code&gt; that you can share with others. This command is ideal for team dashboards.&lt;/p&gt;
&lt;h4 id=&quot;wily-as-a-pre-commit-hook&quot;&gt;&lt;code&gt;wily&lt;/code&gt; as a &lt;code&gt;pre-commit&lt;/code&gt; Hook&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;wily&lt;/code&gt; can be configured so that before you commit changes to your project, it can alert you to improvements or degradations in complexity.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wily&lt;/code&gt; has a &lt;code&gt;wily diff&lt;/code&gt; command, that compares the last indexed data with the current working copy of a file. &lt;/p&gt;
&lt;p&gt;To run a &lt;code&gt;wily diff&lt;/code&gt; command, provide the names of the files you have changed. For example, if I made some changes to &lt;code&gt;requests/api.py&lt;/code&gt; you will see the impact on the metrics by running &lt;code&gt;wily diff&lt;/code&gt; with the file path:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; wily diff requests/api.py 
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the response, you will see all of the changed metrics, as well as the functions or classes that have changed for cyclomatic complexity:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/Screen_Shot_2018-12-29_at_1.58.16_pm.0a6635a82603.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Screen_Shot_2018-12-29_at_1.58.16_pm.0a6635a82603.png&quot; width=&quot;1886&quot; height=&quot;364&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screen_Shot_2018-12-29_at_1.58.16_pm.0a6635a82603.png&amp;amp;w=471&amp;amp;sig=06d2d826ef86d6c03ee4823e9a26a90574149058 471w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Screen_Shot_2018-12-29_at_1.58.16_pm.0a6635a82603.png&amp;amp;w=943&amp;amp;sig=320e018257de950db23d9c85c2e1aadd00d88846 943w, https://files.realpython.com/media/Screen_Shot_2018-12-29_at_1.58.16_pm.0a6635a82603.png 1886w&quot; sizes=&quot;75vw&quot; alt=&quot;Screenshot of the wily diff command&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;diff&lt;/code&gt; command can be paired with a tool called &lt;code&gt;pre-commit&lt;/code&gt;. &lt;code&gt;pre-commit&lt;/code&gt; inserts a hook into your Git configuration that calls a script every time you run the &lt;code&gt;git commit&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;To install &lt;code&gt;pre-commit&lt;/code&gt;, you can install from PyPI:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install pre-commit
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add the following to a &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt; in your projects root directory:&lt;/p&gt;
&lt;div class=&quot;highlight yaml&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;repos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;   &lt;span class=&quot;nt&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;local&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;hooks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;p p-Indicator&quot;&gt;-&lt;/span&gt;   &lt;span class=&quot;nt&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;wily&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;wily&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;wily diff&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;l l-Scalar l-Scalar-Plain&quot;&gt;python&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;additional_dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p p-Indicator&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;wily&lt;/span&gt;&lt;span class=&quot;p p-Indicator&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once setting this, you run the &lt;code&gt;pre-commit install&lt;/code&gt; command to finalize things:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pre-commit install
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whenever you run the &lt;code&gt;git commit&lt;/code&gt; command, it will call &lt;code&gt;wily diff&lt;/code&gt; along with the list of files you&amp;rsquo;ve added to your staged changes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wily&lt;/code&gt; is a useful utility to baseline the complexity of your code and measure the improvements you make when you start to refactor.&lt;/p&gt;
&lt;h2 id=&quot;refactoring-in-python&quot;&gt;Refactoring in Python&lt;/h2&gt;
&lt;p&gt;Refactoring is the technique of changing an application (either the code or the architecture) so that it behaves the same way on the outside, but internally has improved. These improvements can be stability, performance, or reduction in complexity.&lt;/p&gt;
&lt;p&gt;One of the world&amp;rsquo;s oldest underground railways, the London Underground, started in 1863 with the opening of the Metropolitan line. It had gas-lit wooden carriages hauled by steam locomotives. On the opening of the railway, it was fit for purpose. 1900 brought the invention of the electric railways. &lt;/p&gt;
&lt;p&gt;By 1908, the London Underground had expanded to 8 railways. During the Second World War, the London Underground stations were closed to trains and used as air-raid shelters. The modern London Underground carries millions of passengers a day with over 270 stations:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Tube_map_1908-2.d6fefa084c11.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Tube_map_1908-2.d6fefa084c11.jpg&quot; width=&quot;1912&quot; height=&quot;1524&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Tube_map_1908-2.d6fefa084c11.jpg&amp;amp;w=478&amp;amp;sig=2d540e79b39ad2ed83bc60d8e6f28c76d157b14a 478w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Tube_map_1908-2.d6fefa084c11.jpg&amp;amp;w=956&amp;amp;sig=94c38f12c227e34df26f3c5c5ece34970e1f0254 956w, https://files.realpython.com/media/Tube_map_1908-2.d6fefa084c11.jpg 1912w&quot; sizes=&quot;75vw&quot; alt=&quot;London Underground in 1908&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Joint London Underground Railways Map, c. 1908 (Image: &lt;a href=&quot;https://en.wikipedia.org/wiki/History_of_the_London_Underground#/media/File:Tube_map_1908-2.jpg&quot; target=&quot;_blank&quot;&gt;Wikipedia&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;It&amp;rsquo;s almost impossible to write perfect code the first time, and requirements change frequently. If you would have asked the original designers of the railway to design a network fit for 10 million passengers a day in 2020, they would not design the network that exists today.&lt;/p&gt;
&lt;p&gt;Instead, the railway has undergone a series of continuous changes to optimize its operation, design, and layout to match the changes in the city. It has been refactored.&lt;/p&gt;
&lt;p&gt;In this section, you&amp;rsquo;ll explore how to safely refactor by leveraging tests and tools. You&amp;rsquo;ll also see how to use the refactoring functionality in &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; and &lt;a href=&quot;https://www.jetbrains.com/pycharm/&quot;&gt;PyCharm&lt;/a&gt;: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/content_nav_graphic-03.19b0e44dd7c9.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/content_nav_graphic-03.19b0e44dd7c9.png&quot; width=&quot;1668&quot; height=&quot;521&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-03.19b0e44dd7c9.png&amp;amp;w=417&amp;amp;sig=f3424b563ed22b74f2a16a13d36d6f6164516414 417w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-03.19b0e44dd7c9.png&amp;amp;w=834&amp;amp;sig=2f5df7f02a7237b34c9cdbde6d25c9320918cad1 834w, https://files.realpython.com/media/content_nav_graphic-03.19b0e44dd7c9.png 1668w&quot; sizes=&quot;75vw&quot; alt=&quot;Refactoring Section Table of Contents&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;avoiding-risks-with-refactoring-leveraging-tools-and-having-tests&quot;&gt;Avoiding Risks With Refactoring: Leveraging Tools and Having Tests&lt;/h3&gt;
&lt;p&gt;If the point of refactoring is to improve the internals of an application without impacting the externals, how do you ensure the externals haven&amp;rsquo;t changed?&lt;/p&gt;
&lt;p&gt;Before you charge into a major refactoring project, you need to make sure you have a solid test suite for your application. Ideally, that test suite should be mostly automated, so that as you make changes, you see the impact on the user and address it quickly.&lt;/p&gt;
&lt;p&gt;If you want to learn more about testing in Python, &lt;a href=&quot;https://realpython.com/python-testing/&quot;&gt;Getting Started With Testing in Python&lt;/a&gt; is a great place to start.&lt;/p&gt;
&lt;p&gt;There is no perfect number of tests to have on your application. But, the more robust and thorough the test suite, the more aggressively you can refactor your code. &lt;/p&gt;
&lt;p&gt;The two most common tasks you will perform when doing refactoring are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Renaming modules, functions, classes, and methods&lt;/li&gt;
&lt;li&gt;Finding usages of functions, classes, and methods to see where they are called&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can simply do this by hand using &lt;strong&gt;search and replace&lt;/strong&gt;, but it is both time consuming and risky. Instead, there are some great tools to perform these tasks.&lt;/p&gt;
&lt;h3 id=&quot;using-rope-for-refactoring&quot;&gt;Using &lt;code&gt;rope&lt;/code&gt; for Refactoring&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rope&lt;/code&gt; is a free Python utility for refactoring Python code. It comes with an &lt;a href=&quot;https://github.com/python-rope/rope/blob/master/docs/overview.rst&quot;&gt;extensive&lt;/a&gt; set of APIs for refactoring and renaming components in your Python codebase.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rope&lt;/code&gt; can be used in two ways: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;By using an editor plugin, for &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-python.python&quot;&gt;Visual Studio Code&lt;/a&gt;, &lt;a href=&quot;https://github.com/python-rope/ropemacs&quot;&gt;Emacs&lt;/a&gt;, or &lt;a href=&quot;https://github.com/python-rope/ropevim&quot;&gt;Vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Directly by writing scripts to refactor your application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To use rope as a library, first install &lt;code&gt;rope&lt;/code&gt; by executing &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install rope
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It is useful to work with &lt;code&gt;rope&lt;/code&gt; on the REPL so that you can explore the project and see changes in real time. To start, import the &lt;code&gt;Project&lt;/code&gt; type and instantiate it with the path to the project:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rope.base.project&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;requests&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;proj&lt;/code&gt; variable can now perform a series of commands, like &lt;code&gt;get_files&lt;/code&gt; and &lt;code&gt;get_file&lt;/code&gt;, to get a specific file. Get the file &lt;code&gt;api.py&lt;/code&gt; and assign it to a variable called &lt;code&gt;api&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()]&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;structures.py&amp;#39;, &amp;#39;status_codes.py&amp;#39;, ...,&amp;#39;api.py&amp;#39;, &amp;#39;cookies.py&amp;#39;]&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;api.py&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you wanted to rename this file, you could simply rename it on the filesystem. However, any other Python files in your project that imported the old name would now be broken. Let&amp;rsquo;s rename the &lt;code&gt;api.py&lt;/code&gt; to &lt;code&gt;new_api.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rope.refactor.rename&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rename&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;change&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Rename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_changes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;new_api&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;change&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Running &lt;code&gt;git status&lt;/code&gt;, you will see that &lt;code&gt;rope&lt;/code&gt; made some changes to the repository:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; git status
&lt;span class=&quot;go&quot;&gt;On branch master&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Your branch is up to date with &amp;#39;origin/master&amp;#39;.&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Changes not staged for commit:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (use &amp;quot;git add/rm &amp;lt;file&amp;gt;...&amp;quot; to update what will be committed)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (use &amp;quot;git checkout -- &amp;lt;file&amp;gt;...&amp;quot; to discard changes in working directory)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;    modified:   requests/__init__.py&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;    deleted:    requests/api.py&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;Untracked files:&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  (use &amp;quot;git add &amp;lt;file&amp;gt;...&amp;quot; to include in what will be committed)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;   requests/.ropeproject/&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;   requests/new_api.py&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;no changes added to commit (use &amp;quot;git add&amp;quot; and/or &amp;quot;git commit -a&amp;quot;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The three changes made by &lt;code&gt;rope&lt;/code&gt; are the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Deleted &lt;code&gt;requests/api.py&lt;/code&gt; and created &lt;code&gt;requests/new_api.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Modified &lt;code&gt;requests/__init__.py&lt;/code&gt; to import from &lt;code&gt;new_api&lt;/code&gt; instead of &lt;code&gt;api&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Created a project folder named &lt;code&gt;.ropeproject&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To reset the change, run &lt;code&gt;git reset&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are &lt;a href=&quot;https://github.com/python-rope/rope/blob/master/docs/overview.rst#refactorings&quot;&gt;hundreds of other refactorings&lt;/a&gt; that can be done with &lt;code&gt;rope&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-visual-studio-code-for-refactoring&quot;&gt;Using Visual Studio Code for Refactoring&lt;/h3&gt;
&lt;p&gt;Visual Studio Code opens up a small subset of the refactoring commands available in &lt;code&gt;rope&lt;/code&gt; through its own UI.&lt;/p&gt;
&lt;p&gt;You can:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extract variables from a statement&lt;/li&gt;
&lt;li&gt;Extract methods from a block of code&lt;/li&gt;
&lt;li&gt;Sort imports into a logical order&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is an example of using the &lt;em&gt;Extract methods&lt;/em&gt; command from the command palette:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/vscode_extract_method.faccb51c62aa.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/vscode_extract_method.faccb51c62aa.gif&quot; width=&quot;1493&quot; height=&quot;1080&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode_extract_method.faccb51c62aa.gif&amp;amp;w=373&amp;amp;sig=71486d67218dad0476422fe8e7ca093a1843172d 373w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/vscode_extract_method.faccb51c62aa.gif&amp;amp;w=746&amp;amp;sig=f5b90bb6dd14df193c5b0c99c0a9c6ac50b69cb1 746w, https://files.realpython.com/media/vscode_extract_method.faccb51c62aa.gif 1493w&quot; sizes=&quot;75vw&quot; alt=&quot;Screenshot of Visual Studio Code refactoring&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;using-pycharm-for-refactoring&quot;&gt;Using PyCharm for Refactoring&lt;/h3&gt;
&lt;p&gt;If you use or are considering using PyCharm as a Python editor, it&amp;rsquo;s worth taking note of the powerful refactoring capabilities it has.&lt;/p&gt;
&lt;p&gt;You can access all the refactoring shortcuts with the &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-control&quot;&gt;Ctrl&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-t&quot;&gt;T&lt;/kbd&gt;&lt;/span&gt; command on Windows and macOS. The shortcut to access refactoring in Linux is &lt;span class=&quot;keys&quot;&gt;&lt;kbd class=&quot;key-control&quot;&gt;Ctrl&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-shift&quot;&gt;Shift&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-alt&quot;&gt;Alt&lt;/kbd&gt;&lt;span&gt;+&lt;/span&gt;&lt;kbd class=&quot;key-t&quot;&gt;T&lt;/kbd&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;h4 id=&quot;finding-callers-and-usages-of-functions-and-classes&quot;&gt;Finding Callers and Usages of Functions and Classes&lt;/h4&gt;
&lt;p&gt;Before you remove a method or class or change the way it behaves, you&amp;rsquo;ll need to know what code depends on it. PyCharm can search for all usages of a method, function, or class within your project.&lt;/p&gt;
&lt;p&gt;To access this feature, select a method, class, or variable by right-clicking and select &lt;em&gt;Find Usages&lt;/em&gt;: &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_find_usages.0078147136c7.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/pycharm_find_usages.0078147136c7.gif&quot; width=&quot;1493&quot; height=&quot;1080&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_find_usages.0078147136c7.gif&amp;amp;w=373&amp;amp;sig=f5ed5d336240e56036bd890b7255ea6aa5583275 373w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_find_usages.0078147136c7.gif&amp;amp;w=746&amp;amp;sig=c7741d1b1875b6faaa7055c713d8b2c2184971f0 746w, https://files.realpython.com/media/pycharm_find_usages.0078147136c7.gif 1493w&quot; sizes=&quot;75vw&quot; alt=&quot;Finding usages in PyCharm&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All of the code that uses your search criteria is shown in a panel at the bottom. You can double-click on any item to navigate directly to the line in question.&lt;/p&gt;
&lt;h4 id=&quot;using-the-pycharm-refactoring-tools&quot;&gt;Using the PyCharm Refactoring Tools&lt;/h4&gt;
&lt;p&gt;Some of the other refactoring commands include the ability to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extract methods, variables, and constants from existing code&lt;/li&gt;
&lt;li&gt;Extract abstract classes from existing class signatures, including the ability to specify abstract methods&lt;/li&gt;
&lt;li&gt;Rename practically anything, from a variable to a method, file, class, or module&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is an example of renaming the same &lt;code&gt;api.py&lt;/code&gt; module you renamed earlier using the &lt;code&gt;rope&lt;/code&gt; module to &lt;code&gt;new_api.py&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_rename.bc9ce965b901.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/pycharm_rename.bc9ce965b901.gif&quot; width=&quot;1271&quot; height=&quot;1080&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_rename.bc9ce965b901.gif&amp;amp;w=317&amp;amp;sig=169f343bb06a876a4f1aab0dd45766ac2f50a5da 317w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_rename.bc9ce965b901.gif&amp;amp;w=635&amp;amp;sig=cdbb6db8dd394f95fd3654469c36965ee8c73c80 635w, https://files.realpython.com/media/pycharm_rename.bc9ce965b901.gif 1271w&quot; sizes=&quot;75vw&quot; alt=&quot;How to rename methods in pycharm&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The rename command is contextualized to the UI, which makes refactoring quick and simple. It has updated the imports automatically in &lt;code&gt;__init__.py&lt;/code&gt; with the new module name.&lt;/p&gt;
&lt;p&gt;Another useful refactor is the &lt;em&gt;Change Signature&lt;/em&gt; command. This can be used to add, remove, or rename arguments to a function or method. It will search for usages and update them for you:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/pycharm_change_signature.0bffedb78f5c.gif&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/pycharm_change_signature.0bffedb78f5c.gif&quot; width=&quot;1493&quot; height=&quot;1080&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_change_signature.0bffedb78f5c.gif&amp;amp;w=373&amp;amp;sig=87fb7818294abbf129db7c8b3fae034a68a8db68 373w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/pycharm_change_signature.0bffedb78f5c.gif&amp;amp;w=746&amp;amp;sig=69b28a99fa992a45b291aec584ce7d0c697b2729 746w, https://files.realpython.com/media/pycharm_change_signature.0bffedb78f5c.gif 1493w&quot; sizes=&quot;75vw&quot; alt=&quot;Changing method signatures in PyCharm&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can set default values and also decide how the refactoring should handle the new arguments.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Refactoring is an important skill for any developer. As you&amp;rsquo;ve learned in this chapter, you aren&amp;rsquo;t alone. The tools and IDEs already come with powerful refactoring features to be able to make changes quickly.&lt;/p&gt;
&lt;h2 id=&quot;complexity-anti-patterns&quot;&gt;Complexity Anti-Patterns&lt;/h2&gt;
&lt;p&gt;Now that you know how complexity can be measured, how to measure it, and how to refactor your code, it&amp;rsquo;s time to learn 5 common anti-patterns that make code more complex than it need be:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://files.realpython.com/media/content_nav_graphic-04.2d5c9f1c6a60.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/content_nav_graphic-04.2d5c9f1c6a60.png&quot; width=&quot;1667&quot; height=&quot;626&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-04.2d5c9f1c6a60.png&amp;amp;w=416&amp;amp;sig=8893e37e4f71844566190763466c062afa5d55ef 416w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/content_nav_graphic-04.2d5c9f1c6a60.png&amp;amp;w=833&amp;amp;sig=8729d6130b688207ac380559c0ef0fabe3aa6e1c 833w, https://files.realpython.com/media/content_nav_graphic-04.2d5c9f1c6a60.png 1667w&quot; sizes=&quot;75vw&quot; alt=&quot;Anti-Patterns Table of Contents&quot;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you can master these patterns and know how to refactor them, you&amp;rsquo;ll soon be on track (pun intended) to a more maintainable Python application.&lt;/p&gt;
&lt;h3 id=&quot;1-functions-that-should-be-objects&quot;&gt;1. Functions That Should Be Objects&lt;/h3&gt;
&lt;p&gt;Python supports &lt;a href=&quot;https://en.wikipedia.org/wiki/Procedural_programming&quot;&gt;procedural programming&lt;/a&gt; using functions and also &lt;a href=&quot;https://realpython.com/python3-object-oriented-programming/&quot;&gt;inheritable classes&lt;/a&gt;. Both are very powerful and should be applied to different problems.&lt;/p&gt;
&lt;p&gt;Take this example of a module for working with images. The logic in the functions has been removed for brevity:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# imagelib.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;load_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;rb&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;img_lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;crop_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_image_thumbnail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are a few issues with this design:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It&amp;rsquo;s not clear if &lt;code&gt;crop_image()&lt;/code&gt; and &lt;code&gt;get_image_thumbnail()&lt;/code&gt; modify the original &lt;code&gt;image&lt;/code&gt; variable or create new images. If you wanted to load an image then create both a cropped and thumbnail image, would you have to copy the instance first? You could read the source code in the functions, but you can&amp;rsquo;t rely on every developer doing this.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You have to pass the image variable as an argument in every call to the image functions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is how the calling code might look:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;imagelib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crop_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_image_thumbnail&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;~/face.jpg&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;crop_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thumb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_image_thumbnail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here are some symptoms of code using functions that could be refactored into classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Similar arguments across functions&lt;/li&gt;
&lt;li&gt;Higher number of Halstead &lt;code&gt;h2&lt;/code&gt; &lt;strong&gt;unique operands&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Mix of mutable and immutable functions&lt;/li&gt;
&lt;li&gt;Functions spread across multiple Python files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here is a refactored version of those 3 functions, where the following happens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.__init__()&lt;/code&gt; replaces &lt;code&gt;load_image()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;crop()&lt;/code&gt; becomes a class method.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get_image_thumbnail()&lt;/code&gt; becomes a property.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The thumbnail resolution has become a class property, so it can be changed globally or on that particular instance:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# imagelib.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;thumbnail_resolution&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;crop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;thumbnail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thumb&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If there were many more image-related functions in this code, the refactoring to a class could make a drastic change. The next consideration would be the complexity of the consuming code.&lt;/p&gt;
&lt;p&gt;This is how the refactored example would look:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;imagelib&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;~/face.jpg&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;crop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;thumb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thumbnail&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the resulting code, we have solved the original problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is clear that &lt;code&gt;thumbnail&lt;/code&gt; returns a thumbnail since it is a property, and that it doesn&amp;rsquo;t modify the instance.&lt;/li&gt;
&lt;li&gt;The code no longer requires creating new variables for the crop operation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-objects-that-should-be-functions&quot;&gt;2. Objects That Should Be Functions&lt;/h3&gt;
&lt;p&gt;Sometimes, the reverse is true. There is object-oriented code which would be better suited to a simple function or two.&lt;/p&gt;
&lt;p&gt;Here are some tell-tale signs of incorrect use of classes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classes with 1 method (other than &lt;code&gt;.__init__()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Classes that contain only static methods&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take this example of an authentication class:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# authenticate.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Authenticator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It would make more sense to just have a simple function named &lt;code&gt;authenticate()&lt;/code&gt; that takes &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; as arguments:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# authenticate.py&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You don&amp;rsquo;t have to sit down and look for classes that match these criteria by hand: &lt;code&gt;pylint&lt;/code&gt; comes with a rule that classes should have a minimum of 2 public methods. For more on PyLint and other code quality tools, you can check out &lt;a href=&quot;https://realpython.com/python-code-quality/#linters&quot;&gt;Python Code Quality&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To install &lt;code&gt;pylint&lt;/code&gt;, run the following command in your console:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install pylint
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pylint&lt;/code&gt; takes a number of optional arguments and then the path to one or more files and folders. If you run &lt;code&gt;pylint&lt;/code&gt; with its default settings, it&amp;rsquo;s going to give a lot of output as &lt;code&gt;pylint&lt;/code&gt; has a huge number of rules. Instead, you can run specific rules. The &lt;code&gt;too-few-public-methods&lt;/code&gt; rule id is &lt;code&gt;R0903&lt;/code&gt;. You can look this up on the &lt;a href=&quot;https://docs.pylint.org/en/latest/technical_reference/features.html&quot;&gt;documentation website&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pylint --disable&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;all --enable&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;R0903 requests
&lt;span class=&quot;go&quot;&gt;************* Module requests.auth&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests/auth.py:72:0: R0903: Too few public methods (1/2) (too-few-public-methods)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests/auth.py:100:0: R0903: Too few public methods (1/2) (too-few-public-methods)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;************* Module requests.models&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;requests/models.py:60:0: R0903: Too few public methods (1/2) (too-few-public-methods)&lt;/span&gt;

&lt;span class=&quot;go&quot;&gt;-----------------------------------&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;Your code has been rated at 9.99/10&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This output tells us that &lt;code&gt;auth.py&lt;/code&gt; contains 2 classes that have only 1 public method. Those classes are on lines 72 and 100. There is also a class on line 60 of &lt;code&gt;models.py&lt;/code&gt; with only 1 public method.&lt;/p&gt;
&lt;h3 id=&quot;3-converting-triangular-code-to-flat-code&quot;&gt;3. Converting &amp;ldquo;Triangular&amp;rdquo; Code to Flat Code&lt;/h3&gt;
&lt;p&gt;If you were to zoom out on your source code and tilt your head 90 degrees to the right, does the whitespace look flat like Holland or mountainous like the Himalayas? Mountainous code is a sign that your code contains a lot of nesting.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s one of the principles in the &lt;a href=&quot;https://www.python.org/dev/peps/pep-0020/&quot;&gt;&lt;strong&gt;Zen of Python&lt;/strong&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Flat is better than nested&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&amp;mdash; Tim Peters, Zen of Python&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why would flat code be better than nested code? Because nested code makes it harder to read and understand what is happening. The reader has to understand and memorize the conditions as they go through the branches.&lt;/p&gt;
&lt;p&gt;These are the symptoms of highly nested code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A high cyclomatic complexity because of the number of code branches&lt;/li&gt;
&lt;li&gt;A low Maintainability Index because of the high cyclomatic complexity relative to the number of lines of code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take this example that looks at the argument &lt;code&gt;data&lt;/code&gt; for strings that match the word &lt;code&gt;error&lt;/code&gt;. It first checks if the &lt;code&gt;data&lt;/code&gt; argument is a list. Then, it iterates over each and checks if the item is a string. If it is a string and the value is &lt;code&gt;&quot;error&quot;&lt;/code&gt;, then it returns &lt;code&gt;True&lt;/code&gt;. Otherwise, it returns &lt;code&gt;False&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;contains_errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This function would have a low maintainability index because it is small, but it has a high cyclomatic complexity. &lt;/p&gt;
&lt;p&gt;Instead, we can refactor this function by &amp;ldquo;returning early&amp;rdquo; to remove a level of nesting and returning &lt;code&gt;False&lt;/code&gt; if the value of &lt;code&gt;data&lt;/code&gt; is not list. Then using &lt;code&gt;.count()&lt;/code&gt; on the list object to count for instances of &lt;code&gt;&quot;error&quot;&lt;/code&gt;. The return value is then an evaluation that the &lt;code&gt;.count()&lt;/code&gt; is greater than zero:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;contains_errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isinstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;error&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another technique for reducing nesting is to leverage list comprehensions. This common pattern of creating a new list, going through each item in a list to see if it matches a criterion, then adding all matches to the new list:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code can be replaced with a faster and more efficient list comprehension.&lt;/p&gt;
&lt;p&gt;Refactor the last example into a list comprehension and an &lt;code&gt;if&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterable&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This new example is smaller, has less complexity, and is more performant.&lt;/p&gt;
&lt;p&gt;If your data is not a single dimension list, then you can leverage the &lt;a href=&quot;https://docs.python.org/3/library/itertools.html&quot;&gt;itertools&lt;/a&gt; package in the standard library, which contains functions for creating iterators from data structures. You can use it for chaining iterables together, mapping structures, cycling or repeating over existing iterables.&lt;/p&gt;
&lt;p&gt;Itertools also contains functions for filtering data, like &lt;a href=&quot;https://docs.python.org/3/library/itertools.html#itertools.filterfalse&quot;&gt;&lt;code&gt;filterfalse()&lt;/code&gt;&lt;/a&gt;.
For more on Itertools, check out &lt;a href=&quot;https://realpython.com/python-itertools/&quot;&gt;Itertools in Python 3, By Example&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;4-handling-complex-dictionaries-with-query-tools&quot;&gt;4. Handling Complex Dictionaries With Query Tools&lt;/h3&gt;
&lt;p&gt;One of Python&amp;rsquo;s most powerful and widely used core types is the dictionary. It&amp;rsquo;s fast, efficient, scalable, and highly flexible.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to dictionaries, or think you could leverage them more,  you can read &lt;a href=&quot;https://realpython.com/python-dicts/&quot;&gt;Dictionaries in Python&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;It does have one major side-effect: when dictionaries are highly nested, the code that queries them becomes nested too.&lt;/p&gt;
&lt;p&gt;Take this example piece of data, a sample of the Tokyo Metro lines you saw earlier:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;s2&quot;&gt;&amp;quot;network&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;lines&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;name.en&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Ginza&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;name.jp&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;銀座線&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;orange&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;number&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;sign&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;G&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;name.en&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Marunouchi&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;name.jp&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;丸ノ内線&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;red&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;number&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&amp;quot;sign&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;M&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you wanted to get the line that matched a certain number, this could be achieved in a small function:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_line_by_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;number&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ValueError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Line &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{number}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; does not exist.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though the function itself is small, calling the function is unnecessarily complicated because the data is so nested:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_line_by_number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;network&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lines&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are third party tools for querying dictionaries in Python. Some of the most popular are &lt;a href=&quot;http://jmespath.org/&quot;&gt;JMESPath&lt;/a&gt;, &lt;a href=&quot;https://github.com/mahmoud/glom&quot;&gt;glom&lt;/a&gt;, &lt;a href=&quot;https://asq.readthedocs.io/en/latest/&quot;&gt;asq&lt;/a&gt;, and &lt;a href=&quot;https://flupy.readthedocs.io/en/latest/&quot;&gt;flupy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;JMESPath can help with our train network. JMESPath is a querying language designed for JSON, with a plugin available for Python that works with Python dictionaries. To install JMESPath, do the following:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; pip install jmespath
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then open up a Python REPL to explore the JMESPath API, copying in the &lt;code&gt;data&lt;/code&gt; dictionary. To get started, import &lt;code&gt;jmespath&lt;/code&gt; and call &lt;code&gt;search()&lt;/code&gt; with a query string as the first argument and the data as the second. The query string &lt;code&gt;&quot;network.lines&quot;&lt;/code&gt; means return &lt;code&gt;data[&#39;network&#39;][&#39;lines&#39;]&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;jmespath&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmespath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;network.lines&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[{&amp;#39;name.en&amp;#39;: &amp;#39;Ginza&amp;#39;, &amp;#39;name.jp&amp;#39;: &amp;#39;銀座線&amp;#39;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;#39;color&amp;#39;: &amp;#39;orange&amp;#39;, &amp;#39;number&amp;#39;: 3, &amp;#39;sign&amp;#39;: &amp;#39;G&amp;#39;}, &lt;/span&gt;
&lt;span class=&quot;go&quot;&gt; {&amp;#39;name.en&amp;#39;: &amp;#39;Marunouchi&amp;#39;, &amp;#39;name.jp&amp;#39;: &amp;#39;丸ノ内線&amp;#39;,&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;  &amp;#39;color&amp;#39;: &amp;#39;red&amp;#39;, &amp;#39;number&amp;#39;: 4, &amp;#39;sign&amp;#39;: &amp;#39;M&amp;#39;}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When working with lists, you can use square brackets and provide a query inside. The &amp;ldquo;everything&amp;rdquo; query is simply &lt;code&gt;*&lt;/code&gt;. You can then add the name of the attribute inside each matching item to return. If you wanted to get the line number for every line, you could do this:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmespath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;network.lines[*].number&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[3, 4]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can provide more complex queries, like a &lt;code&gt;==&lt;/code&gt; or &lt;code&gt;&amp;lt;&lt;/code&gt;. The syntax is a little unusual for Python developers, so keep the &lt;a href=&quot;http://jmespath.org/examples.html&quot;&gt;documentation&lt;/a&gt; handy for reference.&lt;/p&gt;
&lt;p&gt;If we wanted to find the line with the number &lt;code&gt;3&lt;/code&gt;, this can be done in a single query:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmespath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;network.lines[?number==`3`]&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[{&amp;#39;name.en&amp;#39;: &amp;#39;Ginza&amp;#39;, &amp;#39;name.jp&amp;#39;: &amp;#39;銀座線&amp;#39;, &amp;#39;color&amp;#39;: &amp;#39;orange&amp;#39;, &amp;#39;number&amp;#39;: 3, &amp;#39;sign&amp;#39;: &amp;#39;G&amp;#39;}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we wanted to get the color of that line, you could add the attribute in the end of the query:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jmespath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;network.lines[?number==`3`].color&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;[&amp;#39;orange&amp;#39;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;JMESPath can be used to reduce and simplify code that queries and searches through complex dictionaries.&lt;/p&gt;
&lt;h3 id=&quot;5-using-attrs-and-dataclasses-to-reduce-code&quot;&gt;5. Using &lt;code&gt;attrs&lt;/code&gt; and &lt;code&gt;dataclasses&lt;/code&gt; to Reduce Code&lt;/h3&gt;
&lt;p&gt;Another goal when refactoring is to simply reduce the amount of code in the codebase while achieving the same behaviors. The techniques shown so far can go a long way to refactoring code into smaller and simpler modules.&lt;/p&gt;
&lt;p&gt;Some other techniques require a knowledge of the standard library and some third party libraries.&lt;/p&gt;
&lt;h4 id=&quot;what-is-boilerplate&quot;&gt;What Is Boilerplate?&lt;/h4&gt;
&lt;p&gt;Boilerplate code is code that has to be used in many places with little or no alterations.&lt;/p&gt;
&lt;p&gt;Taking our train network as an example, if we were to convert that into types using Python classes and Python 3 type hints, it might look something like this:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;typing&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name_en&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name_jp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name_en&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name_en&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name_jp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name_jp&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__repr__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;Line &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name_en}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; color=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.color}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39; number=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.number}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; sign=&amp;#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.sign}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39;&amp;gt;&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__str__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;The &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{self.name_en}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; line&amp;quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Network&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lines&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lines&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;@property&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_lines&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, you might also want to add other magic methods, like &lt;code&gt;.__eq__()&lt;/code&gt;. This code is boilerplate. There&amp;rsquo;s no business logic or any other functionality here: we&amp;rsquo;re just copying data from one place to another.&lt;/p&gt;
&lt;h4 id=&quot;a-case-for-dataclasses&quot;&gt;A Case for &lt;code&gt;dataclasses&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Introduced into the standard library in Python 3.7, with a backport package for Python 3.6 on PyPI, the dataclasses module can help remove a lot of boilerplate for these types of classes where you&amp;rsquo;re just storing data.&lt;/p&gt;
&lt;p&gt;To convert the &lt;code&gt;Line&lt;/code&gt; class above to a dataclass, convert all of the fields to class attributes and ensure they have type annotations:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dataclasses&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataclass&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@dataclass&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name_en&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name_jp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can then create an instance of the &lt;code&gt;Line&lt;/code&gt; type with the same arguments as before, with the same fields, and even &lt;code&gt;.__str__()&lt;/code&gt;,  &lt;code&gt;.__repr__()&lt;/code&gt;, and &lt;code&gt;.__eq__()&lt;/code&gt; are implemented:&lt;/p&gt;
&lt;div class=&quot;highlight python pycon&quot;&gt;&lt;span class=&quot;repl-toggle&quot; title=&quot;Toggle REPL prompts and output&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Marunouchi&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;丸ノ内線&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;red&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;color&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;red&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Marunouchi&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;丸ノ内線&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;red&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;M&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;gp&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line2&lt;/span&gt;
&lt;span class=&quot;go&quot;&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Dataclasses are a great way to reduce code with a single import that&amp;rsquo;s already available in the standard library. For a full walkthrough, you can checkout &lt;a href=&quot;https://realpython.com/python-data-classes/&quot;&gt;The Ultimate Guide to Data Classes in Python 3.7&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;some-attrs-use-cases&quot;&gt;Some &lt;code&gt;attrs&lt;/code&gt; Use Cases&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;attrs&lt;/code&gt; is a third party package that&amp;rsquo;s been around a lot longer than dataclasses. &lt;code&gt;attrs&lt;/code&gt; has a lot more functionality, and it&amp;rsquo;s available on Python 2.7 and 3.4+.&lt;/p&gt;
&lt;p&gt;If you are using Python 3.5 or below, &lt;code&gt;attrs&lt;/code&gt; is a great alternative to &lt;code&gt;dataclasses&lt;/code&gt;. Also, it provides many more features.&lt;/p&gt;
&lt;p&gt;The equivalent dataclasses example in &lt;code&gt;attrs&lt;/code&gt; would look similar. Instead of using type annotations, the class attributes are assigned with a value from  &lt;code&gt;attrib()&lt;/code&gt;. This can take additional arguments, such as default values and callbacks for validating input:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@attrs&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name_en&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name_jp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;color&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;attrs&lt;/code&gt; can be a useful package for removing boilerplate code and input validation on data classes.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now that you&amp;rsquo;ve learned how to identify and tackle complicated code, think back to the steps you can now take to make your application easier to change and manage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start off by creating a baseline of your project using a tool like &lt;code&gt;wily&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Look at some of the metrics and start with the module that has the lowest maintainability index.&lt;/li&gt;
&lt;li&gt;Refactor that module using the safety provided in tests and the knowledge of tools like PyCharm and &lt;code&gt;rope&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you follow these steps and the best practices in this article, you can do other exciting things to your application, like adding new features and improving performance.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Traditional Face Detection With Python</title>
      <id>https://realpython.com/traditional-face-detection-python/</id>
      <link href="https://realpython.com/traditional-face-detection-python/"/>
      <updated>2019-02-27T14:00:00+00:00</updated>
      <summary>In this article on face detection with Python, you&#39;ll learn about a historically important algorithm for object detection that can be successfully applied to finding the location of a human face within an image.</summary>
      <content type="html">
        &lt;p&gt;Computer vision is an exciting and growing field. There are tons of interesting problems to solve! One of them is face detection: the ability of a computer to recognize that a photograph contains a human face, and tell you where it is located. In this article, you&amp;rsquo;ll learn about face detection with Python.&lt;/p&gt;
&lt;p&gt;To detect any object in an image, it is necessary to understand how images are represented inside a computer, and how that objects differs &lt;em&gt;visually&lt;/em&gt; from any other object.&lt;/p&gt;
&lt;p&gt;Once that is done, the process of scanning an image and looking for those visual cues needs to be automated and optimized. All these steps come together to form a fast and reliable computer vision algorithm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In this tutorial you&amp;rsquo;ll learn:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What face detection is&lt;/li&gt;
&lt;li&gt;How computers understand features in images&lt;/li&gt;
&lt;li&gt;How to quickly analyze many different features to reach a decision&lt;/li&gt;
&lt;li&gt;How to use a minimal Python solution for detecting human faces in images&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;alert alert-warning&quot; role=&quot;alert&quot;&gt;&lt;p&gt;&lt;strong&gt;Free Bonus:&lt;/strong&gt; &lt;a href=&quot;&quot; class=&quot;alert-link&quot; data-toggle=&quot;modal&quot; data-target=&quot;#modal-python-opencv-examples-experiment&quot; data-focus=&quot;false&quot;&gt;Click here to get the Python Face Detection &amp; OpenCV Examples Mini-Guide&lt;/a&gt; that shows you practical code examples of real-world Python computer vision techniques.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&quot;what-is-face-detection&quot;&gt;What Is Face Detection?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Face detection&lt;/strong&gt; is a type of computer vision technology that is able to identify people&amp;rsquo;s faces within digital images. This is very easy for humans, but computers need precise instructions. The images might contain many objects that aren&amp;rsquo;t human faces, like buildings, cars, animals, and so on.&lt;/p&gt;
&lt;p&gt;It is distinct from other computer vision technologies that involve human faces, like facial recognition, analysis, and tracking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Facial recognition&lt;/strong&gt; involves identifying the face in the image as belonging to person &lt;em&gt;X&lt;/em&gt; and not person &lt;em&gt;Y&lt;/em&gt;. It is often used for biometric purposes, like unlocking your smartphone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Facial analysis&lt;/strong&gt; tries to understand something about people from their facial features, like determining their age, gender, or the emotion they are displaying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Facial tracking&lt;/strong&gt; is mostly present in video analysis and tries to follow a face and its features (eyes, nose, and lips) from frame to frame. The most popular applications are various filters available in mobile apps like Snapchat.&lt;/p&gt;
&lt;p&gt;All of these problems have different technological solutions. This tutorial will focus on a traditional solution for the first challenge: face detection.&lt;/p&gt;
&lt;h2 id=&quot;how-do-computers-see-images&quot;&gt;How Do Computers &amp;ldquo;See&amp;rdquo; Images?&lt;/h2&gt;
&lt;p&gt;The smallest element of an image is called a &lt;strong&gt;pixel&lt;/strong&gt;, or a picture element. It is basically a dot in the picture. An image contains multiple pixels arranged in rows and columns.&lt;/p&gt;
&lt;p&gt;You will often see the number of rows and columns expressed as the image &lt;strong&gt;resolution&lt;/strong&gt;. For example, an Ultra HD TV has the resolution of 3840x2160, meaning it is 3840 pixels wide and 2160 pixels high.&lt;/p&gt;
&lt;p&gt;But a computer does not understand pixels as dots of color. It only understands numbers. To convert colors to numbers, the computer uses various color models.&lt;/p&gt;
&lt;p&gt;In color images, pixels are often represented in the RGB color model. RGB stands for &lt;strong&gt;R&lt;/strong&gt;ed &lt;strong&gt;G&lt;/strong&gt;reen &lt;strong&gt;B&lt;/strong&gt;lue. Each pixel is a mix of those three colors. RGB is great at modeling all the colors humans perceive by combining various amounts of red, green, and blue.&lt;/p&gt;
&lt;p&gt;Since a computer only understand numbers, every pixel is represented by three numbers, corresponding to the amounts of red, green, and blue present in that pixel. You can learn more about color spaces in &lt;a href=&quot;https://realpython.com/python-opencv-color-spaces/&quot;&gt;Image Segmentation Using Color Spaces in OpenCV + Python&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In grayscale (black and white) images, each pixel is a single number, representing the amount of light, or intensity, it carries. In many applications, the range of intensities is from &lt;code&gt;0&lt;/code&gt; (black) to &lt;code&gt;255&lt;/code&gt; (white). Everything between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;255&lt;/code&gt; is various shades of gray.&lt;/p&gt;
&lt;p&gt;If each grayscale pixel is a number, an image is nothing more than a matrix (or table) of numbers:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/3x3.602445ad9862.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/3x3.602445ad9862.png&quot; width=&quot;182&quot; height=&quot;182&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/3x3.602445ad9862.png&amp;amp;w=45&amp;amp;sig=26ed8b334ffd3f3edb925e62fd1c663c190d89bc 45w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/3x3.602445ad9862.png&amp;amp;w=91&amp;amp;sig=e66c55c8399f217e7a2e2a8c974e0e650e9f8bd9 91w, https://files.realpython.com/media/3x3.602445ad9862.png 182w&quot; sizes=&quot;75vw&quot; alt=&quot;Example of pixel values in a 3x3 grayscale image&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Example 3x3 image with pixel values and colors&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;In color images, there are three such matrices representing the red, green, and blue channels.&lt;/p&gt;
&lt;h2 id=&quot;what-are-features&quot;&gt;What Are Features?&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;feature&lt;/strong&gt; is a piece of information in an image that is relevant to solving a certain problem. It could be something as simple as a single pixel value, or more complex like edges, corners, and shapes. You can combine multiple simple features into a complex feature.&lt;/p&gt;
&lt;p&gt;Applying certain operations to an image produces information that could be considered features as well. Computer vision and image processing have a large collection of useful features and feature extracting operations.&lt;/p&gt;
&lt;p&gt;Basically, any inherent or derived property of an image could be used as a feature to solve tasks.&lt;/p&gt;
&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;
&lt;p&gt;To run the code examples, you need to set up an environment with all the necessary libraries installed. The simplest way is to use &lt;a href=&quot;https://conda.io/docs/&quot;&gt;&lt;code&gt;conda&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You will need three libraries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;scikit-image&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scikit-learn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opencv&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To create an environment in &lt;code&gt;conda&lt;/code&gt;, run these commands in your shell:&lt;/p&gt;
&lt;div class=&quot;highlight sh&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; conda create --name face-detection &lt;span class=&quot;nv&quot;&gt;python&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;.7
&lt;span class=&quot;gp&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; activate face-detection
&lt;span class=&quot;gp&quot;&gt;(face-detection)$&lt;/span&gt; conda install scikit-learn
&lt;span class=&quot;gp&quot;&gt;(face-detection)$&lt;/span&gt; conda install -c conda-forge scikit-image
&lt;span class=&quot;gp&quot;&gt;(face-detection)$&lt;/span&gt; conda install -c menpo opencv3
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are having problems installing OpenCV correctly and running the examples, try consulting their &lt;a href=&quot;https://docs.opencv.org/master/da/df6/tutorial_py_table_of_contents_setup.html&quot;&gt;Installation Guide&lt;/a&gt; or the article on &lt;a href=&quot;https://www.pyimagesearch.com/opencv-tutorials-resources-guides/&quot;&gt;OpenCV Tutorials, Resources, and Guides&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now you have all the packages necessary to practice what you learn in this tutorial.&lt;/p&gt;
&lt;h2 id=&quot;viola-jones-object-detection-framework&quot;&gt;Viola-Jones Object Detection Framework&lt;/h2&gt;
&lt;p&gt;This algorithm is named after two computer vision researchers who proposed the method in 2001: Paul Viola and Michael Jones.&lt;/p&gt;
&lt;p&gt;They developed a &lt;a href=&quot;https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf&quot;&gt;general object detection&lt;/a&gt; framework that was able to provide competitive object detection rates in real time. It can be used to solve a variety of detection problems, but the main motivation comes from face detection.&lt;/p&gt;
&lt;p&gt;The Viola-Jones algorithm has 4 main steps, and you&amp;rsquo;ll learn more about each of them in the sections that follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Selecting Haar-like features&lt;/li&gt;
&lt;li&gt;Creating an integral image&lt;/li&gt;
&lt;li&gt;Running AdaBoost training&lt;/li&gt;
&lt;li&gt;Creating classifier cascades&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Given an image, the algorithm looks at many smaller subregions and tries to find a face by looking for specific features in each subregion. It needs to check many different positions and scales because an image can contain many faces of various sizes. Viola and Jones used Haar-like features to detect faces.&lt;/p&gt;
&lt;h3 id=&quot;haar-like-features&quot;&gt;Haar-Like Features&lt;/h3&gt;
&lt;p&gt;All human faces share some similarities. If you look at a photograph showing a person&amp;rsquo;s face, you will see, for example, that the eye region is darker than the bridge of the nose. The cheeks are also brighter than the eye region. We can use these properties to help us understand if an image contains a human face.&lt;/p&gt;
&lt;p&gt;A simple way to find out which region is lighter or darker is to sum up the pixel values of both regions and comparing them. The sum of pixel values in the darker region will be smaller than the sum of pixels in the lighter region. This can be accomplished using &lt;a href=&quot;https://en.wikipedia.org/wiki/Haar-like_feature&quot;&gt;Haar-like features&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A Haar-like feature is represented by taking a rectangular part of an image and dividing that rectangle into multiple parts. They are often visualized as black and white adjacent rectangles:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Haar.885b5c872b35.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Haar.885b5c872b35.png&quot; width=&quot;662&quot; height=&quot;167&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar.885b5c872b35.png&amp;amp;w=165&amp;amp;sig=3b565f5118cc00ad0cb9558a07215be7c741693a 165w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar.885b5c872b35.png&amp;amp;w=331&amp;amp;sig=23c62e053bc92b05f06342f33517c8c7389eeab6 331w, https://files.realpython.com/media/Haar.885b5c872b35.png 662w&quot; sizes=&quot;75vw&quot; alt=&quot;Example of 4 haar-like features&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Basic Haar-like rectangle features&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;In this image, you can see 4 basic types of Haar-like features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Horizontal feature with two rectangles  &lt;/li&gt;
&lt;li&gt;Vertical feature with two rectangles  &lt;/li&gt;
&lt;li&gt;Vertical feature with three rectangles  &lt;/li&gt;
&lt;li&gt;Diagonal feature with four rectangles  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first two examples are useful for detecting edges. The third one detects lines, and the fourth one is good for finding diagonal features. But how do they work?&lt;/p&gt;
&lt;p&gt;The value of the feature is calculated as a single number: the sum of pixel values in the black area minus the sum of pixel values in the white area. For uniform areas like a wall, this number would be close to zero and won&amp;rsquo;t give you any meaningful information.&lt;/p&gt;
&lt;p&gt;To be useful, a Haar-like feature needs to give you a large number, meaning that the areas in the black and white rectangles are very different. There are known features that perform very well to detect human faces:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Haar_Feature_eyes.46f3184a76ab.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Haar_Feature_eyes.46f3184a76ab.jpg&quot; width=&quot;997&quot; height=&quot;293&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar_Feature_eyes.46f3184a76ab.jpg&amp;amp;w=249&amp;amp;sig=9875bd3eb076242c9115e52e996255020b74568f 249w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar_Feature_eyes.46f3184a76ab.jpg&amp;amp;w=498&amp;amp;sig=4f79cd65e07891cb2a12ba7d3e1304473dd20bf5 498w, https://files.realpython.com/media/Haar_Feature_eyes.46f3184a76ab.jpg 997w&quot; sizes=&quot;75vw&quot; alt=&quot;Haar-like feature applied on the human eyes&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Haar-like feature applied on the eye region. (Image: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Haar_Feature_that_looks_similar_to_the_eye_region_which_is_darker_than_the_upper_cheeks_is_applied_onto_a_face.jpg#/media/File:Haar_Feature_that_looks_similar_to_the_eye_region_which_is_darker_than_the_upper_cheeks_is_applied_onto_a_face.jpg&quot; target=&quot;_blank&quot;&gt;Wikipedia&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;In this example, the eye region is darker than the region below. You can use this property to find which areas of an image give a strong response (large number) for a specific feature:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Haar_Feature_nose.eccdca63eb5a.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Haar_Feature_nose.eccdca63eb5a.jpg&quot; width=&quot;982&quot; height=&quot;291&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar_Feature_nose.eccdca63eb5a.jpg&amp;amp;w=245&amp;amp;sig=9c2be226d028e5bab21a90fa00209900d9fcb1b1 245w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Haar_Feature_nose.eccdca63eb5a.jpg&amp;amp;w=491&amp;amp;sig=7b84bc986552c0cc96c20a669b10689cee10ffd7 491w, https://files.realpython.com/media/Haar_Feature_nose.eccdca63eb5a.jpg 982w&quot; sizes=&quot;75vw&quot; alt=&quot;Haar-like feature applied on the bridge of the nose&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Haar-like feature applied on the bridge of the nose. (Image: &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Haar_Feature_that_looks_similar_to_the_bridge_of_the_nose_is_applied_onto_the_face.jpg#/media/File:Haar_Feature_that_looks_similar_to_the_bridge_of_the_nose_is_applied_onto_the_face.jpg&quot; target=&quot;_blank&quot;&gt;Wikipedia&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This example gives a strong response when applied to the bridge of the nose. You can combine many of these features to understand if an image region contains a human face.&lt;/p&gt;
&lt;p&gt;As mentioned, the Viola-Jones algorithm calculates a lot of these features in many subregions of an image. This quickly becomes computationally expensive: it takes a lot of time using the limited resources of a computer.&lt;/p&gt;
&lt;p&gt;To tackle this problem, Viola and Jones used integral images.&lt;/p&gt;
&lt;h3 id=&quot;integral-images&quot;&gt;Integral Images&lt;/h3&gt;
&lt;p&gt;An integral image (also known as a summed-area table) is the name of both a data structure and an algorithm used to obtain this data structure. It is used as a quick and efficient way to calculate the sum of pixel values in an image or rectangular part of an image.&lt;/p&gt;
&lt;p&gt;In an integral image, the value of each point is the sum of all pixels above and to the left, including the target pixel:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Integral_image.ff570b17c188.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Integral_image.ff570b17c188.png&quot; width=&quot;602&quot; height=&quot;242&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Integral_image.ff570b17c188.png&amp;amp;w=150&amp;amp;sig=da0504595bfb938b8ff1d4dee067fdf09f87e541 150w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Integral_image.ff570b17c188.png&amp;amp;w=301&amp;amp;sig=f7482aceb9ebcc2699fe704063df4a22bf027ec9 301w, https://files.realpython.com/media/Integral_image.ff570b17c188.png 602w&quot; sizes=&quot;75vw&quot; alt=&quot;Calculating an integral image from pixel values&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Calculating an integral image from pixel values&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The integral image can be calculated in a single pass over the original image. This reduces summing the pixel intensities within a rectangle into only three operations with four numbers, regardless of rectangle size:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/ABCD.97ca0ef04d39.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/ABCD.97ca0ef04d39.png&quot; width=&quot;257&quot; height=&quot;287&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ABCD.97ca0ef04d39.png&amp;amp;w=64&amp;amp;sig=823b04bf358063ebe85e7ccc09d20218a566842f 64w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ABCD.97ca0ef04d39.png&amp;amp;w=128&amp;amp;sig=a826703e0638450caeb2e2c290912f7f8ce7fe40 128w, https://files.realpython.com/media/ABCD.97ca0ef04d39.png 257w&quot; sizes=&quot;75vw&quot; alt=&quot;Selecting a rectangle in an integral image&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Calculate the sum of pixels in the orange rectangle.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The sum of pixels in the rectangle &lt;em&gt;ABCD&lt;/em&gt; can be derived from the values of points &lt;em&gt;A&lt;/em&gt;, &lt;em&gt;B&lt;/em&gt;, &lt;em&gt;C&lt;/em&gt;, and &lt;em&gt;D&lt;/em&gt;, using the formula &lt;em&gt;D - B - C + A&lt;/em&gt;. It is easier to understand this formula visually:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/ABCD-2.c181d3f76349.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/ABCD-2.c181d3f76349.png&quot; width=&quot;558&quot; height=&quot;678&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ABCD-2.c181d3f76349.png&amp;amp;w=139&amp;amp;sig=fabbc8f483fdb0d363e024976ed7c110398d9ff1 139w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/ABCD-2.c181d3f76349.png&amp;amp;w=279&amp;amp;sig=fdc9996d8a47ac47b8b41e8bbdce0d32d685b296 279w, https://files.realpython.com/media/ABCD-2.c181d3f76349.png 558w&quot; sizes=&quot;75vw&quot; alt=&quot;Step by step calculation of a rectangle in an integral image&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Calculating the sum of pixels step by step&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;You&amp;rsquo;ll notice that subtracting both &lt;em&gt;B&lt;/em&gt; and &lt;em&gt;C&lt;/em&gt; means that the area defined with &lt;em&gt;A&lt;/em&gt; has been subtracted twice, so we need to add it back again.&lt;/p&gt;
&lt;p&gt;Now you have a simple way to calculate the difference between the sums of pixel values of two rectangles. This is perfect for Haar-like features!&lt;/p&gt;
&lt;p&gt;But how do you decide which of these features and in what sizes to use for finding faces in images? This is solved by a machine learning algorithm called &lt;strong&gt;boosting&lt;/strong&gt;. Specifically, you will learn about AdaBoost, short for &lt;strong&gt;Adaptive Boosting&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;adaboost&quot;&gt;AdaBoost&lt;/h3&gt;
&lt;p&gt;Boosting is based on the following question: &amp;ldquo;Can a set of &lt;strong&gt;weak learners&lt;/strong&gt; create a single &lt;strong&gt;strong learner&lt;/strong&gt;?&amp;rdquo; A weak learner (or weak classifier) is defined as a classifier that is only slightly better than random guessing.&lt;/p&gt;
&lt;p&gt;In face detection, this means that a weak learner can classify a subregion of an image as a face or not-face only slightly better than random guessing. A strong learner is substantially better at picking faces from non-faces.&lt;/p&gt;
&lt;p&gt;The power of boosting comes from combining many (thousands) of weak classifiers into a single strong classifier. In the Viola-Jones algorithm, each Haar-like feature represents a weak learner. To decide the type and size of a feature that goes into the final classifier, AdaBoost checks the performance of all classifiers that you supply to it.&lt;/p&gt;
&lt;p&gt;To calculate the performance of a classifier, you evaluate it on all subregions of all the images used for training. Some subregions will produce a strong response in the classifier. Those will be classified as positives, meaning the classifier thinks it contains a human face.&lt;/p&gt;
&lt;p&gt;Subregions that don&amp;rsquo;t produce a strong response don&amp;rsquo;t contain a human face, in the classifiers opinion. They will be classified as negatives.&lt;/p&gt;
&lt;p&gt;The classifiers that performed well are given higher importance or &lt;strong&gt;weight&lt;/strong&gt;. The final result is a strong classifier, also called a &lt;strong&gt;boosted classifier&lt;/strong&gt;, that contains the best performing weak classifiers.&lt;/p&gt;
&lt;p&gt;The algorithm is called adaptive because, as training progresses, it gives more emphasis on those images that were incorrectly classified. The weak classifiers that perform better on these hard examples are weighted more strongly than others.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at an example:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-1.f0eacdf0c2e2.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-1.f0eacdf0c2e2.png&quot; width=&quot;321&quot; height=&quot;241&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-1.f0eacdf0c2e2.png&amp;amp;w=80&amp;amp;sig=c2e77f8aaa0e718f71693ee4f305af1a74538dba 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-1.f0eacdf0c2e2.png&amp;amp;w=160&amp;amp;sig=1dd70c553a39b6a709647e0c54b0d0aa1615eeb4 160w, https://files.realpython.com/media/AdaBoost-1.f0eacdf0c2e2.png 321w&quot; sizes=&quot;75vw&quot; alt=&quot;Example of features to classify with AdaBoost&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The blue and orange circles are samples that belong to different categories.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Imagine that you are supposed to classify blue and orange circles in the following image using a set of weak classifiers:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-2.03c3c6a0c34f.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-2.03c3c6a0c34f.png&quot; width=&quot;321&quot; height=&quot;283&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-2.03c3c6a0c34f.png&amp;amp;w=80&amp;amp;sig=1c89820133dd088552530713c0c31b3e30294327 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-2.03c3c6a0c34f.png&amp;amp;w=160&amp;amp;sig=d8ce222be5a8a32fea58fa2160a4848811e122ac 160w, https://files.realpython.com/media/AdaBoost-2.03c3c6a0c34f.png 321w&quot; sizes=&quot;75vw&quot; alt=&quot;First classifier in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The first weak classifier classifies some of the blue circles correctly.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The first classifier you use captures some of the blue circles but misses the others. In the next iteration, you give more importance to the missed examples:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-3.2d874993bc92.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-3.2d874993bc92.png&quot; width=&quot;321&quot; height=&quot;283&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-3.2d874993bc92.png&amp;amp;w=80&amp;amp;sig=d5739e656092fae72dcdab8b98fb9a9926f05dbc 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-3.2d874993bc92.png&amp;amp;w=160&amp;amp;sig=ac483a1235e50457d27f12e08268b760b658dab5 160w, https://files.realpython.com/media/AdaBoost-3.2d874993bc92.png 321w&quot; sizes=&quot;75vw&quot; alt=&quot;Changing the importance of samples in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The missed blue samples are given more importance, indicated by size.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The second classifier that manages to correctly classify those examples will get a higher weight. Remember, if a weak classifier performs better, it will get a higher weight and thus higher chances to be included in the final, strong classifiers:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-4.705558b218f3.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-4.705558b218f3.png&quot; width=&quot;321&quot; height=&quot;283&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-4.705558b218f3.png&amp;amp;w=80&amp;amp;sig=3b6398cb10162b31923cfacf3ffcfb940f5e290d 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-4.705558b218f3.png&amp;amp;w=160&amp;amp;sig=02ec5a9cbac83c4a8e395ca69ef0eeb991bbb186 160w, https://files.realpython.com/media/AdaBoost-4.705558b218f3.png 321w&quot; sizes=&quot;75vw&quot; alt=&quot;Second classifier in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The second classifier captures the bigger blue circles.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Now you have managed to capture all of the blue circles, but incorrectly captured some of the orange circles. These incorrectly classified orange circles are given more importance in the next iteration:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-5.1d800ca4ffff.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-5.1d800ca4ffff.png&quot; width=&quot;321&quot; height=&quot;283&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-5.1d800ca4ffff.png&amp;amp;w=80&amp;amp;sig=b5a2727e0a0c25471956d28c148738475d8b72a3 80w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-5.1d800ca4ffff.png&amp;amp;w=160&amp;amp;sig=957516ce6698a73fc7ed2d13a620be3216b3c06f 160w, https://files.realpython.com/media/AdaBoost-5.1d800ca4ffff.png 321w&quot; sizes=&quot;75vw&quot; alt=&quot;Changing the importance of samples in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The misclassified orange circles are given more importance, and others are reduced.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The final classifier manages to capture those orange circles correctly:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-6.ac0a9accb5f7.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-6.ac0a9accb5f7.png&quot; width=&quot;363&quot; height=&quot;241&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-6.ac0a9accb5f7.png&amp;amp;w=90&amp;amp;sig=ef08275aa27f5f7531bb1f5b32f633b28a8763c6 90w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-6.ac0a9accb5f7.png&amp;amp;w=181&amp;amp;sig=3fdd3847748fb3bb23ae6ea9a729736933e66ab5 181w, https://files.realpython.com/media/AdaBoost-6.ac0a9accb5f7.png 363w&quot; sizes=&quot;75vw&quot; alt=&quot;Third classifier in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The third classifier captures the remaining orange circles.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;To create a strong classifier, you combine all three classifiers to correctly classify all examples:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/AdaBoost-7.2ec2db197252.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/AdaBoost-7.2ec2db197252.png&quot; width=&quot;363&quot; height=&quot;362&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-7.2ec2db197252.png&amp;amp;w=90&amp;amp;sig=1ff28a3b66cefb11032affbbf24eb5710a1d5849 90w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/AdaBoost-7.2ec2db197252.png&amp;amp;w=181&amp;amp;sig=83bb6b20f9a76de0ca5d2781912b6b6386550010 181w, https://files.realpython.com/media/AdaBoost-7.2ec2db197252.png 363w&quot; sizes=&quot;75vw&quot; alt=&quot;Final, strong classifier in the AdaBoost example&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;The final, strong classifier combines all three weak classifiers.&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Using a variation of this process, Viola and Jones have evaluated hundreds of thousands of classifiers that specialize in finding faces in images. But it would be computationally expensive to run all these classifiers on every region in every image, so they created something called a &lt;strong&gt;classifier cascade&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cascading-classifiers&quot;&gt;Cascading Classifiers&lt;/h3&gt;
&lt;p&gt;The definition of a cascade is a series of waterfalls coming one after another. A similar concept is used in computer science to solve a complex problem with simple units. The problem here is reducing the number of computations for each image.&lt;/p&gt;
&lt;p&gt;To solve it, Viola and Jones turned their strong classifier (consisting of thousands of weak classifiers) into a cascade where each weak classifier represents one stage. The job of the cascade is to quickly discard non-faces and avoid wasting precious time and computations.&lt;/p&gt;
&lt;p&gt;When an image subregion enters the cascade, it is evaluated by the first stage. If that stage evaluates the subregion as positive, meaning that it thinks it&amp;rsquo;s a face, the output of the stage is &lt;em&gt;maybe&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If a subregion gets a &lt;em&gt;maybe&lt;/em&gt;, it is sent to the next stage of the cascade. If that one gives a positive evaluation, then that&amp;rsquo;s another &lt;em&gt;maybe&lt;/em&gt;, and the image is sent to the third stage:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/one_stage.901b5d130118.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block w-66&quot; src=&quot;https://files.realpython.com/media/one_stage.901b5d130118.png&quot; width=&quot;656&quot; height=&quot;221&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/one_stage.901b5d130118.png&amp;amp;w=164&amp;amp;sig=602e78643540b5dd04c8bb669f305821bc62e91b 164w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/one_stage.901b5d130118.png&amp;amp;w=328&amp;amp;sig=864b79754185489b0e6ae889f47de7b875a63fdd 328w, https://files.realpython.com/media/one_stage.901b5d130118.png 656w&quot; sizes=&quot;75vw&quot; alt=&quot;A single classifier in a cascade&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;A weak classifier in a cascade&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This process is repeated until the image passes through all stages of the cascade. If all classifiers approve the image, it is finally classified as a human face and is presented to the user as a detection.&lt;/p&gt;
&lt;p&gt;If, however, the first stage gives a negative evaluation, then the image is immediately discarded as not containing a human face. If it passes the first stage but fails the second stage, it is discarded as well. Basically, the image can get discarded at any stage of the classifier:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/Classifier_cascade.e3b2a5652044.png&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/Classifier_cascade.e3b2a5652044.png&quot; width=&quot;1596&quot; height=&quot;471&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Classifier_cascade.e3b2a5652044.png&amp;amp;w=399&amp;amp;sig=95df5c9be0496a237be01e5b4ad7f1e34c3c5ab9 399w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/Classifier_cascade.e3b2a5652044.png&amp;amp;w=798&amp;amp;sig=58b01c34d1d034ab5bc96eea3978046d9ad66b13 798w, https://files.realpython.com/media/Classifier_cascade.e3b2a5652044.png 1596w&quot; sizes=&quot;75vw&quot; alt=&quot;Cascading classifiers for face detection&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;A cascade of _n_ classifiers for face detection&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;This is designed so that non-faces get discarded very quickly, which saves a lot of time and computational resources. Since every classifier represents a feature of a human face, a positive detection basically says, &amp;ldquo;Yes, this subregion contains all the features of a human face.&amp;rdquo; But as soon as one feature is missing, it rejects the whole subregion.&lt;/p&gt;
&lt;p&gt;To accomplish this effectively, it is important to put your best performing classifiers early in the cascade. In the Viola-Jones algorithm, the eyes and nose bridge classifiers are examples of best performing weak classifiers.&lt;/p&gt;
&lt;p&gt;Now that you understand how the algorithm works, it is time to use it to detect faces with Python.&lt;/p&gt;
&lt;h3 id=&quot;using-a-viola-jones-classifier&quot;&gt;Using a Viola-Jones Classifier&lt;/h3&gt;
&lt;p&gt;Training a Viola-Jones classifier from scratch can take a long time. Fortunately, a pre-trained Viola-Jones classifier comes out-of-the-box with OpenCV! You will use that one to see the algorithm in action.&lt;/p&gt;
&lt;p&gt;First, find and download an image that you would like to scan for the presence of human faces. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/bla2.5577e4ec1f8e.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/bla2.5577e4ec1f8e.jpg&quot; width=&quot;1037&quot; height=&quot;691&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bla2.5577e4ec1f8e.jpg&amp;amp;w=259&amp;amp;sig=bc93c7a92507b852caf7edb82c10919920018d0c 259w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bla2.5577e4ec1f8e.jpg&amp;amp;w=518&amp;amp;sig=dc4e40b662ec714acdc440ebe910cc233a46336d 518w, https://files.realpython.com/media/bla2.5577e4ec1f8e.jpg 1037w&quot; sizes=&quot;75vw&quot; alt=&quot;Family stock photo&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Example stock photo for face detection (&lt;a href=&quot;https://www.pexels.com/photo/standing-family-near-fireplace-1648387/&quot; target=&quot;_blank&quot;&gt;Image source&lt;/a&gt;)&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Import OpenCV and load the image into memory:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cv2&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;cv&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Read image from your local file system&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;original_image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imread&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/your-image.jpg&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Convert color image to grayscale for Viola-Jones&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grayscale_image&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cvtColor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;original_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;COLOR_BGR2GRAY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, you need to load the Viola-Jones classifier. If you installed OpenCV from source, it will be in the folder where you installed the OpenCV library.&lt;/p&gt;
&lt;p&gt;Depending on the version, the exact path might vary, but the folder name will be &lt;code&gt;haarcascades&lt;/code&gt;, and it will contain multiple files. The one you need is called &lt;code&gt;haarcascade_frontalface_alt.xml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If for some reason, your installation of OpenCV did not get the pre-trained classifier, you can obtain it from the &lt;a href=&quot;https://github.com/opencv/opencv/tree/master/data/haarcascades&quot;&gt;OpenCV GitHub repo&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;# Load the classifier and create a cascade object for face detection&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;face_cascade&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CascadeClassifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;path/to/haarcascade_frontalface_alt.xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;face_cascade&lt;/code&gt; object has a method &lt;code&gt;detectMultiScale()&lt;/code&gt;, which receives an image as an argument and runs the classifier cascade over the image. The term &lt;strong&gt;MultiScale&lt;/strong&gt; indicates that the algorithm looks at subregions of the image in multiple scales, to detect faces of varying sizes:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;detected_faces&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;face_cascade&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;detectMultiScale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grayscale_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The variable &lt;code&gt;detected_faces&lt;/code&gt; now contains all the detections for the target image. To visualize the detections, you need to iterate over all detections and draw rectangles over the detected faces.&lt;/p&gt;
&lt;p&gt;OpenCV&amp;rsquo;s &lt;code&gt;rectangle()&lt;/code&gt; draws rectangles over images, and it needs to know the pixel coordinates of the top-left and bottom-right corner. The coordinates indicate the row and column of pixels in the image.&lt;/p&gt;
&lt;p&gt;Luckily, detections are saved as pixel coordinates. Each detection is defined by its top-left corner coordinates and width and height of the rectangle that encompasses the detected face. &lt;/p&gt;
&lt;p&gt;Adding the width to the row and height to the column will give you the bottom-right corner of the image:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;detected_faces&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rectangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;original_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;rectangle()&lt;/code&gt; accepts the following arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The original image&lt;/li&gt;
&lt;li&gt;The coordinates of the top-left point of the detection&lt;/li&gt;
&lt;li&gt;The coordinates of the bottom-right point of the detection&lt;/li&gt;
&lt;li&gt;The color of the rectangle (a tuple that defines the amount of red, green, and blue (&lt;code&gt;0&lt;/code&gt;-&lt;code&gt;255&lt;/code&gt;))&lt;/li&gt;
&lt;li&gt;The thickness of the rectangle lines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, you need to display the image:&lt;/p&gt;
&lt;div class=&quot;highlight python&quot;&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imshow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Image&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;original_image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destroyAllWindows&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;imshow()&lt;/code&gt; displays the image. &lt;code&gt;waitKey()&lt;/code&gt; waits for a keystroke. Otherwise, &lt;code&gt;imshow()&lt;/code&gt; would display the image and immediately close the window. Passing &lt;code&gt;0&lt;/code&gt; as the argument tells it to wait indefinitely. Finally, &lt;code&gt;destroyAllWindows()&lt;/code&gt; closes the window when you press a key.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the result:&lt;/p&gt;
&lt;figure class=&quot;figure mx-auto d-block&quot;&gt;&lt;a href=&quot;https://files.realpython.com/media/bla3.0a8b11f62c76.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img class=&quot;img-fluid mx-auto d-block &quot; src=&quot;https://files.realpython.com/media/bla3.0a8b11f62c76.jpg&quot; width=&quot;1037&quot; height=&quot;691&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bla3.0a8b11f62c76.jpg&amp;amp;w=259&amp;amp;sig=a711c55cb3ab37d22cb1f55fced6f20156cd690c 259w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/bla3.0a8b11f62c76.jpg&amp;amp;w=518&amp;amp;sig=a23966099ba3660d4a0739c9b2d0a39b4e6152b6 518w, https://files.realpython.com/media/bla3.0a8b11f62c76.jpg 1037w&quot; sizes=&quot;75vw&quot; alt=&quot;Family stock photo with detected faces&quot;/&gt;&lt;/a&gt;&lt;figcaption class=&quot;figure-caption text-center&quot;&gt;Original image with detections&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;That&amp;rsquo;s it! You now have a ready-to-use face detector in Python.&lt;/p&gt;
&lt;p&gt;If you really want to train the classifier yourself, &lt;code&gt;scikit-image&lt;/code&gt; offers a tutorial with the accompanying code &lt;a href=&quot;http://scikit-image.org/docs/dev/auto_examples/xx_applications/plot_haar_extraction_selection_classification.html&quot;&gt;on their website&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;The Viola-Jones algorithm was an amazing achievement. Even though it still performs great for many use cases, it is almost 20 years old. Other algorithms exist, and they use different features&lt;/p&gt;
&lt;p&gt;One example uses support vector machines (SVM) and features called histograms of oriented gradients (HOG). An example can be found in the &lt;a href=&quot;https://jakevdp.github.io/PythonDataScienceHandbook/05.14-image-features.html&quot;&gt;Python Data Science Handbook&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most current state-of-the-art methods for face detection and recognition use deep learning, which we will cover in a follow-up article.&lt;/p&gt;
&lt;p&gt;For state-of-the-art computer vision research, have a look at the recent scientific articles on arXiv&amp;rsquo;s &lt;a href=&quot;https://arxiv.org/list/cs.CV/recent&quot;&gt;Computer Vision and Pattern Recognition&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in machine learning but want to switch to something other than computer vision, check out &lt;a href=&quot;https://realpython.com/python-keras-text-classification/&quot;&gt;Practical Text Classification With Python and Keras&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Good work! You are now able to  find faces in images.&lt;/p&gt;
&lt;p&gt;In this tutorial, you have learned how to represent regions in an image with Haar-like features. These features can be calculated very quickly using integral images.&lt;/p&gt;
&lt;p&gt;You have learned how AdaBoost finds the best performing Haar-like features from thousands of available features and turns them into a series of weak classifiers. &lt;/p&gt;
&lt;p&gt;Finally, you have learned how to create a cascade of weak classifiers that can quickly and reliably distinguish faces from non-faces.&lt;/p&gt;
&lt;p&gt;These steps illustrate many important elements of computer vision:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Finding useful features&lt;/li&gt;
&lt;li&gt;Combining them to solve complex problems&lt;/li&gt;
&lt;li&gt;Balancing between performance and managing computational resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These ideas apply to object detection in general and will help you solve many real-world challenges. Good luck!&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  
    <entry>
      <title>Python Community Interview With Ali Spittel</title>
      <id>https://realpython.com/interview-ali-spittel/</id>
      <link href="https://realpython.com/interview-ali-spittel/"/>
      <updated>2019-02-25T14:00:00+00:00</updated>
      <summary>Ali Spittel is a Pythonista with a passion for CSS art and teaching Python. Join us as we talk about her non-traditional path to learning to code, teaching at a Bootcamp, and her recent move to one of the most positive developer communities around.</summary>
      <content type="html">
        &lt;p&gt;Happy 2019! For our first community interview of the year, I&amp;rsquo;m joined by &lt;a href=&quot;https://www.alispit.tel/#/&quot;&gt;Ali Spittel&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Ali is a Pythonista with a passion for CSS art and teaching Python. Join us as we talk about her non-traditional path to learning to code, teaching at a Bootcamp, and her recent move to one of the most positive developer communities around.  &lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Welcome, Ali! Thank you for joining me for this interview. Let’s start with the usual first questions. How’d you get into programming? When did you start using Python?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; Thanks for having me! I got into programming during my sophomore year of college. I randomly went to a computer science class because I had an extra credit hour for the semester.&lt;/p&gt;
&lt;p&gt;It was a Python class, and I fell in love with it. I decided part way through the semester to try to double major in computer science. They even offered me a teaching assistant job for the next semester!&lt;/p&gt;
&lt;p&gt;Then, the next semester, I took Data Structures and Algorithms in C++. I hated it, and I had to put so much work into the class. I decided coding wasn&amp;rsquo;t for me, and so I quit programming. I only got back into it because I was offered a paid programming internship and couldn&amp;rsquo;t find one in politics, the field I was interested in at the time!&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid w-25 float-right ml-3&quot; src=&quot;https://files.realpython.com/media/selfie-2deb4d287f21467a088a85560b909533-0ab41.4aaa90ee9a93.jpg&quot; width=&quot;500&quot; height=&quot;757&quot; srcset=&quot;https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/selfie-2deb4d287f21467a088a85560b909533-0ab41.4aaa90ee9a93.jpg&amp;amp;w=125&amp;amp;sig=5d90eb113d4d3ce6895e7210acb3bb0cdceb131b 125w, https://robocrop.realpython.net/?url=https%3A//files.realpython.com/media/selfie-2deb4d287f21467a088a85560b909533-0ab41.4aaa90ee9a93.jpg&amp;amp;w=250&amp;amp;sig=dd0a9521f037f54bae43725cf8e2e8b3cf3ad2c6 250w, https://files.realpython.com/media/selfie-2deb4d287f21467a088a85560b909533-0ab41.4aaa90ee9a93.jpg 500w&quot; sizes=&quot;75vw&quot; alt=&quot;Ali Spittel&quot;/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Until recently, you were the Lead Instructor for the Web Development Immersive program at &lt;a href=&quot;https://generalassemb.ly/&quot;&gt;General Assembly&lt;/a&gt; in Washington, D.C.&lt;/em&gt;&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;em&gt;I’m curious to know what it’s like to teach at a coding Bootcamp and how it has changed as Bootcamps become more common and accepted as a non-traditional path to becoming a developer?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; Teaching is one of the most rewarding things I&amp;rsquo;ve ever done. Seeing material click for students and seeing their growth is awesome. I also love building a curriculum and shaping how people see code at the beginning of their journeys.&lt;/p&gt;
&lt;p&gt;I think Bootcamps are so important for increasing tech diversity, and there are so many great programs within Bootcamps that serve people from economically disadvantaged or underrepresented demographic groups in tech.&lt;/p&gt;
&lt;p&gt;For my last 9 months working at GA, I worked for their enterprise program, where I was teaching onsite for companies who were training employees.&lt;/p&gt;
&lt;p&gt;I love that those companies were really investing in their employees and that those students could really focus on their education instead of having to worry about the financial aspects of doing a coding Bootcamp. &lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m actually still teaching their Python part-time program in D.C. even though I don&amp;rsquo;t teach full-time anymore!&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Teaching seems to be something you enjoy doing, between working as a Teaching Assistant in college and working at the General Assembly. Last year, you expanded on that and &lt;a href=&quot;https://zen-of-programming.com/&quot;&gt;started a blog&lt;/a&gt; that has become really popular. What made you decide to start the blog, and has success you’ve had early on with it surprised you?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; I started writing pretty randomly. I had an idea for a CSS art blog post, and then decided I could go further with it and write a series about learning new things. I started out on Medium, where I got next to no traction, and then found out about &lt;a href=&quot;https://dev.to/&quot;&gt;DEV.to&lt;/a&gt; a little later on, where more people were reading my writing.&lt;/p&gt;
&lt;p&gt;I fell out of writing for a few months because I was getting more and more speaking roles, and I was more focused on that.&lt;/p&gt;
&lt;p&gt;Then this summer I moved to Connecticut for work, and I didn&amp;rsquo;t know a lot of people. I had a concept for a blog that was a hybrid of a programming blog and a lifestyle blog. Those posts started to really gain some traction, which was awesome.&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You’re also a member of the D.C. meetup circuit, and most notably, the D.C. chapter of &lt;a href=&quot;https://twitter.com/WomenWhoCodeDC&quot;&gt;Women Who Code&lt;/a&gt;, which now has almost 9000 members! How is the meetup scene in D.C., particularly for women and minorities? What advice would you give to someone who is looking to go to their first meetup?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; The meetup scene in DC is great! There&amp;rsquo;s an event pretty much every night (usually multiple), and I always learn so much from them and meet great people. &lt;/p&gt;
&lt;p&gt;My advice for someone going to a meetup for the first time would be to bring a friend if you&amp;rsquo;re nervous the first time! I still pretty much always make sure I know somebody before heading to a meetup. It just makes it easier when I have at least one person to talk to.&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;When we first met on &lt;a href=&quot;https://twitter.com/ASpittel&quot;&gt;Twitter&lt;/a&gt;, you were posting daily code challenges from &lt;a href=&quot;https://www.codewars.com/&quot;&gt;code wars&lt;/a&gt;, and then solving them in Python. It was great to see so much participation from others, as they compared answers in various languages. Are you still doing those? What did you get out of doing something like that with the community?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; I loved doing the code challenges on Twitter! I did them for six months, I think, and would love to get back into them.&lt;/p&gt;
&lt;p&gt;Unfortunately, my life is a little busy right now between a new puppy and a new job! I may be able to start putting them up again soon though. I have a lot of fun with them. &lt;/p&gt;
&lt;p&gt;I did them for a few reasons: first to prove that I can actually code since I have had people doubt that, and second because they&amp;rsquo;re fun to do. Hopefully they&amp;rsquo;re educational to some people!&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;You just alluded to your new job. Why don&amp;rsquo;t you tell us more about your exciting news and the reason for the change?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; I just started a new job last week! I&amp;rsquo;m now working as a frontend developer and doing developer advocacy work for &lt;a href=&quot;https://dev.to/&quot;&gt;DEV.to&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to be working on their open source codebase, which will be awesome. On top of that, I&amp;rsquo;ll be doing more speaking and writing for them, as well as developer community outreach!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m so excited to work for them because they&amp;rsquo;re such an awesome community. Their tagline is &lt;em&gt;Where programmers share ideas and help each other grow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It is an online community for sharing and discovering great ideas, having debates, and making friends. It&amp;rsquo;s definitely been that way for me as a community member. It was a huge part of how I gained a reader-base for my blog&amp;mdash;their users are so supportive. I&amp;rsquo;m so excited to be a bigger part of the community!&lt;/p&gt;
&lt;p mt-5=&quot;mt-5&quot;&gt;&lt;strong&gt;Ricky:&lt;/strong&gt; &lt;em&gt;Now for my last question. What else do you get up to in your spare time? What other hobbies and interests do you have, aside from Python and coding?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ali:&lt;/strong&gt; In my spare time, I rock climb, check out the awesome food and bar scene in D.C., and hang out with my puppy!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Thank you Ali for taking the time to talk to me, and good luck in your new role! To keep up with Ali, you can &lt;a href=&quot;https://zen-of-programming.com/&quot;&gt;check out her blog&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/ASpittel&quot;&gt;follow her on Twitter&lt;/a&gt;, or check out her awesome content on &lt;a href=&quot;https://dev.to/aspittel&quot;&gt;DEV.to&lt;/a&gt;.&lt;/p&gt;
        &lt;hr /&gt;
        &lt;p&gt;&lt;em&gt;[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short &amp;amp; sweet Python Trick delivered to your inbox every couple of days. &lt;a href=&quot;https://realpython.com/python-tricks/?utm_source=realpython&amp;amp;utm_medium=rss&amp;amp;utm_campaign=footer&quot;&gt;&amp;gt;&amp;gt; Click here to learn more and see examples&lt;/a&gt; ]&lt;/em&gt;&lt;/p&gt;
      </content>
    </entry>
  

</feed>
